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
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from copy import copy
|
|
4
|
+
from itertools import groupby
|
|
5
|
+
|
|
6
|
+
import undetected_chromedriver as uc
|
|
7
|
+
import websockets
|
|
8
|
+
|
|
9
|
+
from aiohttp import ClientResponse
|
|
10
|
+
from asyncio import run, sleep
|
|
11
|
+
from base64 import b64encode
|
|
12
|
+
from cryptography.hazmat.primitives import padding
|
|
13
|
+
from cryptography.hazmat.primitives.ciphers import Cipher
|
|
14
|
+
from cryptography.hazmat.primitives.ciphers.algorithms import AES
|
|
15
|
+
from cryptography.hazmat.primitives.ciphers.modes import CBC
|
|
16
|
+
from datetime import datetime, timedelta
|
|
17
|
+
from hashlib import sha256
|
|
18
|
+
from json import dumps, loads
|
|
19
|
+
from math import ceil
|
|
20
|
+
from os import urandom
|
|
21
|
+
from payeer_api import PayeerAPI
|
|
22
|
+
from re import search
|
|
23
|
+
from selenium.webdriver.common.by import By
|
|
24
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
|
25
|
+
from selenium.webdriver.support import expected_conditions as ec
|
|
26
|
+
from selenium.webdriver.common.action_chains import ActionChains
|
|
27
|
+
from urllib.parse import urlencode
|
|
28
|
+
from x_client.aiohttp import Client
|
|
29
|
+
from xync_bot import XyncBot
|
|
30
|
+
from xync_schema import models
|
|
31
|
+
|
|
32
|
+
from xync_client.loader import PAY_TOKEN
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def encrypt_data(data: dict, md5digest: bytes):
|
|
36
|
+
# Convert data to JSON string (equivalent to json_encode)
|
|
37
|
+
bdata = dumps(data).encode()
|
|
38
|
+
|
|
39
|
+
# Generate random IV (16 bytes for AES)
|
|
40
|
+
iv = urandom(16)
|
|
41
|
+
|
|
42
|
+
# Pad or truncate key to 32 bytes
|
|
43
|
+
if len(md5digest) < 32:
|
|
44
|
+
md5digest = md5digest.ljust(32, b"\0") # Pad with null bytes
|
|
45
|
+
elif len(md5digest) > 32:
|
|
46
|
+
md5digest = md5digest[:32] # Truncate to 32 bytes
|
|
47
|
+
|
|
48
|
+
# Apply PKCS7 padding
|
|
49
|
+
padder = padding.PKCS7(128).padder() # 128 bits = 16 bytes block size
|
|
50
|
+
padded_data = padder.update(bdata)
|
|
51
|
+
padded_data += padder.finalize()
|
|
52
|
+
|
|
53
|
+
# Create cipher
|
|
54
|
+
cipher = Cipher(AES(md5digest), CBC(iv))
|
|
55
|
+
encryptor = cipher.encryptor()
|
|
56
|
+
|
|
57
|
+
# Encrypt
|
|
58
|
+
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
|
|
59
|
+
|
|
60
|
+
return iv + ciphertext
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
rep = {
|
|
64
|
+
"USDT": "UST",
|
|
65
|
+
"DOGE": "DOG",
|
|
66
|
+
"DASH": "DAA",
|
|
67
|
+
"MANA": "MA2",
|
|
68
|
+
"METIS": "ME2",
|
|
69
|
+
"AUDIO": "AU2",
|
|
70
|
+
"MASK": "MA3",
|
|
71
|
+
"SUPER": "SU2",
|
|
72
|
+
"USDP": "US2",
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class PmAgentClient(Client):
|
|
77
|
+
host = "payeer.com/"
|
|
78
|
+
headers = {
|
|
79
|
+
"x-requested-with": "XMLHttpRequest",
|
|
80
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
81
|
+
}
|
|
82
|
+
cookies = {"isAuthorized": "true"}
|
|
83
|
+
api: PayeerAPI
|
|
84
|
+
pages: dict[str, str] = {
|
|
85
|
+
"login": f"https://{host}en/auth/",
|
|
86
|
+
"home": f"https://{host}en/account/",
|
|
87
|
+
}
|
|
88
|
+
_ceils: tuple[float, float]
|
|
89
|
+
|
|
90
|
+
balances: dict[str, float] = {} # {cur: available}
|
|
91
|
+
orders: dict[int, dict] = defaultdict() # {(quote, cur): (price, amount)}
|
|
92
|
+
orders_dict: dict[str, dict[str, dict]] = defaultdict() # {(quote, cur): (price, amount)}
|
|
93
|
+
|
|
94
|
+
def __init__(self, agent: models.PmAgent, bot: XyncBot = None):
|
|
95
|
+
self.agent = agent
|
|
96
|
+
self.bot = bot
|
|
97
|
+
super().__init__(self.host, cookies=self.agent.state["cookies"])
|
|
98
|
+
# if api_id := agent.auth.get("api_id"):
|
|
99
|
+
# self.api = PayeerAPI(agent.auth["email"], api_id, agent.auth["api_sec"])
|
|
100
|
+
# if trade_id := agent.auth.get("trade_id"):
|
|
101
|
+
# self.tapi = PayeerTradeAPI(trade_id, agent.auth["trade_sec"])
|
|
102
|
+
# create_task(self.heartbeat())
|
|
103
|
+
|
|
104
|
+
async def login(self):
|
|
105
|
+
async def _save():
|
|
106
|
+
self.agent.state["cookies"] = {c["name"]: c["value"] for c in cd.get_cookies() if c["name"] == "PHPSESSID"}
|
|
107
|
+
self.agent.state["sessid"] = search(r"'bitrix_sessid':'([0-9a-f]{32})'", cd.page_source).group(1)
|
|
108
|
+
if not self.agent.auth.get("UserID"):
|
|
109
|
+
self.agent.auth["UserID"] = search(r"/?UserID=(\d{7,8})&", cd.page_source).group(1)
|
|
110
|
+
await self.agent.save()
|
|
111
|
+
super().__init__(self.host, cookies=self.agent.state["cookies"])
|
|
112
|
+
|
|
113
|
+
options = uc.ChromeOptions()
|
|
114
|
+
options.add_argument("--disable-blink-features=AutomationControlled")
|
|
115
|
+
options.add_argument("--no-sandbox")
|
|
116
|
+
options.add_argument("--disable-dev-shm-usage")
|
|
117
|
+
options.add_argument("--headless=new") # for Chrome >= 109
|
|
118
|
+
options.add_argument("--disable-renderer-backgrounding")
|
|
119
|
+
options.add_argument("--disable-background-timer-throttling")
|
|
120
|
+
options.add_argument("--disable-backgrounding-occluded-windows")
|
|
121
|
+
options.add_argument("--disable-client-side-phishing-detection")
|
|
122
|
+
options.add_argument("--disable-crash-reporter")
|
|
123
|
+
options.add_argument("--disable-oopr-debug-crash-dump")
|
|
124
|
+
options.add_argument("--no-crash-upload")
|
|
125
|
+
options.add_argument("--disable-gpu")
|
|
126
|
+
options.add_argument("--disable-extensions")
|
|
127
|
+
options.add_argument("--disable-low-res-tiling")
|
|
128
|
+
options.add_argument("--log-level=3")
|
|
129
|
+
options.add_argument("--silent")
|
|
130
|
+
options.add_argument("--window-size=1920,1080")
|
|
131
|
+
options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
|
132
|
+
cd = uc.Chrome(
|
|
133
|
+
options=options,
|
|
134
|
+
headless=False,
|
|
135
|
+
browser_executable_path="/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta",
|
|
136
|
+
)
|
|
137
|
+
wait = WebDriverWait(cd, timeout=15)
|
|
138
|
+
cd.get("https://payeer.com/en/")
|
|
139
|
+
wait.until(ec.invisibility_of_element_located((By.TAG_NAME, "lottie-player")))
|
|
140
|
+
login_link = wait.until(ec.element_to_be_clickable((By.CLASS_NAME, "button.button_empty")))
|
|
141
|
+
login_link.click()
|
|
142
|
+
email_field = wait.until(ec.presence_of_element_located((By.NAME, "email")))
|
|
143
|
+
email_field.send_keys(self.agent.auth.get("email"))
|
|
144
|
+
password_field = wait.until(ec.presence_of_element_located((By.NAME, "password")))
|
|
145
|
+
password_field.send_keys(self.agent.auth.get("password"))
|
|
146
|
+
login_button = wait.until(ec.element_to_be_clickable((By.CLASS_NAME, "login-form__login-btn.step1")))
|
|
147
|
+
await sleep(1)
|
|
148
|
+
login_button.click()
|
|
149
|
+
await sleep(4)
|
|
150
|
+
# check if logged in
|
|
151
|
+
if not cd.current_url.startswith(self.pages["login"]):
|
|
152
|
+
if cd.current_url.startswith(self.pages["home"]):
|
|
153
|
+
return await _save()
|
|
154
|
+
# we are NOT logged in
|
|
155
|
+
login_button.click()
|
|
156
|
+
await sleep(1)
|
|
157
|
+
if (v := cd.find_elements(By.CLASS_NAME, "form-input-top")) and v[0].text == "Введите проверочный код":
|
|
158
|
+
code = input("Email code: ")
|
|
159
|
+
actions = ActionChains(cd)
|
|
160
|
+
for char in code:
|
|
161
|
+
actions.send_keys(char).perform()
|
|
162
|
+
step2_button = wait.until(ec.element_to_be_clickable((By.CLASS_NAME, "login-form__login-btn.step2")))
|
|
163
|
+
step2_button.click()
|
|
164
|
+
# check if logged in
|
|
165
|
+
if not cd.current_url.startswith(self.pages["login"]):
|
|
166
|
+
if cd.current_url.startswith(self.pages["home"]):
|
|
167
|
+
return await _save()
|
|
168
|
+
# we are NOT logged in
|
|
169
|
+
await self.bot.send(193017646, "Payeer not logged in", photo=cd.get_screenshot_as_png())
|
|
170
|
+
cd.quit()
|
|
171
|
+
raise Exception("Payeer not logged in")
|
|
172
|
+
|
|
173
|
+
async def heartbeat(self):
|
|
174
|
+
"""Фоновая задача для PING/PONG"""
|
|
175
|
+
url = "bitrix/components/payeer/account.info2/templates/top2/ajax.php"
|
|
176
|
+
params = {"action": "balance2", "sessid": self.agent.state["sessid"], "_": int(time.time() * 1000)}
|
|
177
|
+
resp = await self.session.get(url, params=params)
|
|
178
|
+
if not resp.content.total_bytes: # check
|
|
179
|
+
await self.login()
|
|
180
|
+
params["sessid"] = self.agent.state["sessid"]
|
|
181
|
+
while self.agent.active:
|
|
182
|
+
await sleep(30)
|
|
183
|
+
params["_"] = int(time.time() * 1000)
|
|
184
|
+
bb = await self._get("bitrix/components/payeer/account.info2/templates/top2/ajax.php", params)
|
|
185
|
+
avlb = {c: fb for c, b in bb["balance"].items() if (fb := float(".".join([b[0].replace(",", ""), b[1]])))}
|
|
186
|
+
print(avlb, end=" ")
|
|
187
|
+
await self.agent.refresh_from_db()
|
|
188
|
+
await self.stop()
|
|
189
|
+
|
|
190
|
+
async def send(self, recip: str, amount: float, cur_id: int) -> int | list:
|
|
191
|
+
cur = await models.Cur[cur_id]
|
|
192
|
+
data = {
|
|
193
|
+
"payout_method": 1136053,
|
|
194
|
+
"template_pay": "",
|
|
195
|
+
"param_ACCOUNT_NUMBER": recip,
|
|
196
|
+
"comment": "",
|
|
197
|
+
"protect_code": "",
|
|
198
|
+
"protect_day": 1,
|
|
199
|
+
"sum_receive": amount,
|
|
200
|
+
"curr_receive": cur.ticker,
|
|
201
|
+
"sum_pay": round(amount * 1.005, 2),
|
|
202
|
+
"curr_pay": cur.ticker,
|
|
203
|
+
"mfa_code": "",
|
|
204
|
+
"master_key": "",
|
|
205
|
+
"block": 0,
|
|
206
|
+
"ps": 1136053,
|
|
207
|
+
"sign": "",
|
|
208
|
+
"output_type": "list",
|
|
209
|
+
"fee_0": "N",
|
|
210
|
+
"sessid": self.agent.state["sessid"],
|
|
211
|
+
}
|
|
212
|
+
url = "bitrix/components/payeer/account.send.08_18/templates/as_add11/ajax.php?action=output"
|
|
213
|
+
res = await self._post(url, form_data=data)
|
|
214
|
+
if res["success"]:
|
|
215
|
+
return res["id"]
|
|
216
|
+
return res["error"]
|
|
217
|
+
|
|
218
|
+
async def check_in(self, amount: float, cur: str, dt: datetime = None, tid: int = None) -> dict[int, float]:
|
|
219
|
+
history = self.api.history(type="incoming", count=10)
|
|
220
|
+
if tid:
|
|
221
|
+
return (t := history.get(tid)) and {t["id"]: float(t["creditedAmount"])}
|
|
222
|
+
return {
|
|
223
|
+
h["id"]: float(h["creditedAmount"])
|
|
224
|
+
for h in history.values()
|
|
225
|
+
if (
|
|
226
|
+
amount <= float(h["creditedAmount"]) <= ceil(amount)
|
|
227
|
+
and h["creditedCurrency"] == cur
|
|
228
|
+
and datetime.fromisoformat(h["date"]) > dt - timedelta(minutes=3) # +180(tz)-5 # todo: wrong tz
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async def ws(self, quote: str, fiat: str): # pair="UST_RUB"
|
|
233
|
+
pair = "_".join(rep.get(c, c[:2] + "1") if len(c) > 3 else c for c in [quote, fiat])
|
|
234
|
+
uid = self.agent.auth["UserID"]
|
|
235
|
+
psid = self.agent.state["cookies"]["PHPSESSID"]
|
|
236
|
+
ws_url = f"wss://payeer.com/wss/socket.io/?UserID={uid}&auth_hash={psid}&EIO=4&transport=websocket"
|
|
237
|
+
_sig = None
|
|
238
|
+
bids: dict[float, float] = {}
|
|
239
|
+
asks: dict[float, float] = {}
|
|
240
|
+
async with websockets.connect(ws_url) as websocket:
|
|
241
|
+
while resp := await websocket.recv():
|
|
242
|
+
if resp.startswith("0"):
|
|
243
|
+
await websocket.send("40")
|
|
244
|
+
elif resp.startswith("40"):
|
|
245
|
+
_sig = loads(resp.lstrip("40"))["sid"]
|
|
246
|
+
await websocket.send('42["pair change",{"pair":"' + pair + '"}]')
|
|
247
|
+
elif resp == "2": # ping?
|
|
248
|
+
await websocket.send("3") # pong?
|
|
249
|
+
elif (resp := resp.lstrip("42")) and resp.startswith("["):
|
|
250
|
+
data = loads(resp)
|
|
251
|
+
topic, data = data.pop(0), data.pop(0)
|
|
252
|
+
if topic == "toall":
|
|
253
|
+
if dif := data["data"].get("diff"): # это обновление стакана (мейкеры изменили ставки)
|
|
254
|
+
am_acc = 0
|
|
255
|
+
bid_list = [
|
|
256
|
+
(float(p), am, am_acc := am_acc + am)
|
|
257
|
+
for p, a, r, _ in dif[0]
|
|
258
|
+
if (am := float(a)) > 2 and am_acc < 500
|
|
259
|
+
]
|
|
260
|
+
old_bids: dict[float, float] = copy(bids)
|
|
261
|
+
bids = {price: amount for price, amount, acc in bid_list}
|
|
262
|
+
bids_dif = {p: ad for p, a in bids.items() if round(ad := a - old_bids.get(p, 0), 2)}
|
|
263
|
+
am_acc = 0
|
|
264
|
+
ask_list = [
|
|
265
|
+
(float(p), am, am_acc := am_acc + am)
|
|
266
|
+
for p, a, r, _ in dif[1]
|
|
267
|
+
if (am := float(a)) > 2 and am_acc < 500
|
|
268
|
+
]
|
|
269
|
+
old_asks: dict[float, float] = copy(asks)
|
|
270
|
+
asks = {price: amount for price, amount, acc in ask_list}
|
|
271
|
+
asks_dif = {p: ad for p, a in asks.items() if round(ad := a - old_asks.get(p, 0), 2)}
|
|
272
|
+
|
|
273
|
+
self._ceils = bid_list[0][0], ask_list[0][0]
|
|
274
|
+
base = round(sum(self._ceils) / 2, 3)
|
|
275
|
+
spread = self._ceils[0] - self._ceils[1]
|
|
276
|
+
pspread = spread / base * 100
|
|
277
|
+
print(round(base, 4), round(spread, 2), f"{pspread:.2f}%")
|
|
278
|
+
print(bids, bids_dif)
|
|
279
|
+
print(asks, asks_dif)
|
|
280
|
+
elif data["handler"] == "onTradeHistory":
|
|
281
|
+
side = int(data["data"]["history"][0][1])
|
|
282
|
+
hist = {
|
|
283
|
+
time: {float(h[2]): float(h[3]) for h in g}
|
|
284
|
+
for time, g in groupby(data["data"]["history"], key=lambda x: x[0])
|
|
285
|
+
}
|
|
286
|
+
print(side, hist)
|
|
287
|
+
# (asks if side else bids)
|
|
288
|
+
else:
|
|
289
|
+
raise ValueError(resp)
|
|
290
|
+
|
|
291
|
+
async def get_ceils(self) -> tuple[float, float]: # inside ceils + fee
|
|
292
|
+
return self._ceils[0] * (1 + self.agent.pm.fee), self._ceils[1] * (1 - self.agent.pm.fee)
|
|
293
|
+
|
|
294
|
+
@staticmethod
|
|
295
|
+
def form_redirect(topup: models.TopUp) -> tuple[str, dict | None]:
|
|
296
|
+
m_shop = str(topup.topupable.auth["id"])
|
|
297
|
+
m_orderid = str(topup.id)
|
|
298
|
+
m_amount = "{0:.2f}".format(topup.amount * 0.01)
|
|
299
|
+
m_curr = topup.cur.ticker
|
|
300
|
+
m_desc = b64encode(b"XyncPay top up").decode()
|
|
301
|
+
m_key = topup.topupable.auth["sec"]
|
|
302
|
+
data = [m_shop, m_orderid, m_amount, m_curr, m_desc]
|
|
303
|
+
# # additional
|
|
304
|
+
# m_params = {
|
|
305
|
+
# 'success_url': 'https://xync.net/topup?success=1',
|
|
306
|
+
# 'fail_url': 'https://xync.net/topup?success=0',
|
|
307
|
+
# 'status_url': 'https://xync.net/topup',
|
|
308
|
+
# 'reference': {'var1': '1'},
|
|
309
|
+
# }
|
|
310
|
+
#
|
|
311
|
+
# key = md5(m_orderid.to_bytes()).digest()
|
|
312
|
+
#
|
|
313
|
+
# base64url_encode(encrypt_data(params, key))
|
|
314
|
+
#
|
|
315
|
+
# data.append(m_params)
|
|
316
|
+
# # additional
|
|
317
|
+
|
|
318
|
+
data.append(m_key)
|
|
319
|
+
|
|
320
|
+
sign = sha256(":".join(data).encode()).hexdigest().upper()
|
|
321
|
+
|
|
322
|
+
params = {
|
|
323
|
+
"m_shop": m_shop,
|
|
324
|
+
"m_orderid": m_orderid,
|
|
325
|
+
"m_amount": m_amount,
|
|
326
|
+
"m_curr": m_curr,
|
|
327
|
+
"m_desc": m_desc,
|
|
328
|
+
"m_sign": sign,
|
|
329
|
+
# 'm_params': m_params,
|
|
330
|
+
# 'm_cipher_method': 'AES-256-CBC-IV',
|
|
331
|
+
"form[ps]": "2609",
|
|
332
|
+
"form[curr[2609]]": m_curr,
|
|
333
|
+
}
|
|
334
|
+
url = "https://payeer.com/merchant/?" + urlencode(params)
|
|
335
|
+
return url, None
|
|
336
|
+
|
|
337
|
+
def get_topup(self, tid: str) -> dict:
|
|
338
|
+
hi = self.api.get_history_info(tid)
|
|
339
|
+
ti = self.api.shop_order_info(hi["params"]["SHOP_ID"], hi["params"]["ORDER_ID"])["info"]
|
|
340
|
+
return ti["status"] == "execute" and {
|
|
341
|
+
"pmid": ti["id"],
|
|
342
|
+
"from_acc": hi["params"]["ACCOUNT_NUMBER"],
|
|
343
|
+
"oid": hi["params"]["ORDER_ID"],
|
|
344
|
+
"amount": int(float(ti["sumOut"]) * 100),
|
|
345
|
+
"ts": datetime.strptime(ti["dateCreate"], "%d.%m.%Y %H:%M:%S") - timedelta(hours=3),
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async def _proc(self, resp: ClientResponse, bp=None) -> dict | str:
|
|
349
|
+
if resp.status == 200:
|
|
350
|
+
await resp.read()
|
|
351
|
+
# noinspection PyProtectedMember
|
|
352
|
+
return loads(resp._body.strip()) # payeer bug: returns json with content-type=html
|
|
353
|
+
raise Exception(resp)
|
|
354
|
+
|
|
355
|
+
async def balance_load(self):
|
|
356
|
+
self.balances = await self.tapi.acc()
|
|
357
|
+
|
|
358
|
+
async def orders_load(self):
|
|
359
|
+
self.orders = {
|
|
360
|
+
int(oid): {
|
|
361
|
+
"ts": o["date"],
|
|
362
|
+
"pair": o["pair"].split("_"),
|
|
363
|
+
"is_sell": o["action"] == "sell",
|
|
364
|
+
"amount": float(o["amount"]),
|
|
365
|
+
"price": float(o["price"]),
|
|
366
|
+
"value": float(o["value"]),
|
|
367
|
+
"amount_proc": float(o["amount_processed"]),
|
|
368
|
+
"value_proc": float(o["value_processed"]),
|
|
369
|
+
}
|
|
370
|
+
for oid, o in (await self.tapi.orders()).items()
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
async def main():
|
|
375
|
+
from x_model import init_db
|
|
376
|
+
from xync_client.loader import TORM
|
|
377
|
+
|
|
378
|
+
cn = await init_db(TORM, True)
|
|
379
|
+
agent = await models.PmAgent.get_or_none(pm__norm="payeer", user_id=1).prefetch_related("user", "pm")
|
|
380
|
+
# tapi = PayeerTradeAPI(agent.auth['trade_id'], agent.auth['trade_sec'])
|
|
381
|
+
# b = await tapi.acc()
|
|
382
|
+
# rub_am, usdt_am = b['RUB']['available'], b['USDT']['available']
|
|
383
|
+
# r = await tapi.trade('USDT_RUB', False, 10, 118.01)
|
|
384
|
+
bbot = XyncBot(PAY_TOKEN, cn)
|
|
385
|
+
cl: PmAgentClient = agent.client(bbot)
|
|
386
|
+
await cl.ws("USDT", "RUB")
|
|
387
|
+
|
|
388
|
+
while agent.active:
|
|
389
|
+
await sleep(75)
|
|
390
|
+
await cl.send("P1135398755", 10, 1)
|
|
391
|
+
await agent.refresh_from_db()
|
|
392
|
+
await cl.stop()
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
if __name__ == "__main__":
|
|
396
|
+
run(main())
|
xync_client/Pms/Payeer/login.py
CHANGED
|
@@ -1,60 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import undetected_chromedriver as uc
|
|
3
|
-
from xync_schema.models import PmAgent
|
|
4
|
-
from selenium.webdriver.support.ui import WebDriverWait
|
|
5
|
-
from selenium.webdriver.support import expected_conditions as EC
|
|
6
|
-
from selenium.webdriver.common.action_chains import ActionChains
|
|
7
|
-
import time
|
|
1
|
+
# login_url = "https://payeer.com/bitrix/components/auth/system.auth.authorize/templates/.default/ajax.php"
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
async def login(agent: PmAgent):
|
|
11
|
-
options = uc.ChromeOptions()
|
|
12
|
-
options.add_argument("--disable-blink-features=AutomationControlled")
|
|
13
|
-
options.add_argument("--no-sandbox")
|
|
14
|
-
options.add_argument("--disable-dev-shm-usage")
|
|
15
|
-
options.add_argument("--headless=new") # for Chrome >= 109
|
|
16
|
-
options.add_argument("--disable-renderer-backgrounding")
|
|
17
|
-
options.add_argument("--disable-background-timer-throttling")
|
|
18
|
-
options.add_argument("--disable-backgrounding-occluded-windows")
|
|
19
|
-
options.add_argument("--disable-client-side-phishing-detection")
|
|
20
|
-
options.add_argument("--disable-crash-reporter")
|
|
21
|
-
options.add_argument("--disable-oopr-debug-crash-dump")
|
|
22
|
-
options.add_argument("--no-crash-upload")
|
|
23
|
-
options.add_argument("--disable-gpu")
|
|
24
|
-
options.add_argument("--disable-extensions")
|
|
25
|
-
options.add_argument("--disable-low-res-tiling")
|
|
26
|
-
options.add_argument("--log-level=3")
|
|
27
|
-
options.add_argument("--silent")
|
|
28
|
-
options.add_argument("--window-size=1920,1080")
|
|
29
|
-
options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
|
30
|
-
|
|
31
|
-
driver = uc.Chrome(options=options, headless=True)
|
|
32
|
-
wait = WebDriverWait(driver, timeout=10)
|
|
33
|
-
try:
|
|
34
|
-
driver.get("https://payeer.com/en/auth")
|
|
35
|
-
wait.until(EC.invisibility_of_element_located((By.TAG_NAME, "lottie-player")))
|
|
36
|
-
login_link = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "button.button_empty")))
|
|
37
|
-
login_link.click()
|
|
38
|
-
email_field = wait.until(EC.presence_of_element_located((By.NAME, "email")))
|
|
39
|
-
email_field.send_keys(agent.auth.get("email"))
|
|
40
|
-
password_field = wait.until(EC.presence_of_element_located((By.NAME, "password")))
|
|
41
|
-
password_field.send_keys(agent.auth.get("password"))
|
|
42
|
-
login_button = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "login-form__login-btn.step1")))
|
|
43
|
-
login_button.click()
|
|
44
|
-
time.sleep(4)
|
|
45
|
-
try:
|
|
46
|
-
login_button.click()
|
|
47
|
-
except Exception:
|
|
48
|
-
pass
|
|
49
|
-
time.sleep(1)
|
|
50
|
-
if (v := driver.find_elements(By.CLASS_NAME, "form-input-top")) and v[0].text == "Введите проверочный код":
|
|
51
|
-
code = input("Email code: ")
|
|
52
|
-
actions = ActionChains(driver)
|
|
53
|
-
for char in code:
|
|
54
|
-
actions.send_keys(char).perform()
|
|
55
|
-
step2_button = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "login-form__login-btn.step2")))
|
|
56
|
-
step2_button.click()
|
|
57
|
-
agent.state = {"cookies": driver.get_cookies()}
|
|
58
|
-
await agent.save()
|
|
59
|
-
finally:
|
|
60
|
-
driver.quit()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import hmac
|
|
3
|
+
|
|
4
|
+
from json import dumps
|
|
5
|
+
from time import time
|
|
6
|
+
|
|
7
|
+
from x_client.aiohttp import Client
|
|
8
|
+
|
|
9
|
+
maker_fee = 0.01
|
|
10
|
+
taker_fee = 0.095
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PayeerTradeAPI(Client):
|
|
14
|
+
sec: bytes
|
|
15
|
+
|
|
16
|
+
def __init__(self, key: str, sec: str):
|
|
17
|
+
self.sec = sec.encode()
|
|
18
|
+
super().__init__("payeer.com/", headers={"API-ID": key})
|
|
19
|
+
|
|
20
|
+
async def _req(self, method: str, data: dict):
|
|
21
|
+
h = hmac.new(self.sec, digestmod=hashlib.sha256)
|
|
22
|
+
data["ts"] = int(time() * 1000)
|
|
23
|
+
sdata = method + dumps(data)
|
|
24
|
+
h.update(sdata.encode())
|
|
25
|
+
sign = h.hexdigest()
|
|
26
|
+
return await self._post("api/trade/" + method, data, hdrs={"API-SIGN": sign})
|
|
27
|
+
|
|
28
|
+
async def acc(self) -> dict[str, dict[str, float]]:
|
|
29
|
+
res = await self._req("account", {})
|
|
30
|
+
return res["success"] and {c: b for c, b in res["balances"].items() if b["total"]}
|
|
31
|
+
|
|
32
|
+
async def orders(self) -> dict[str, dict]:
|
|
33
|
+
res = await self._req("my_orders", {})
|
|
34
|
+
return res["success"] and res["items"]
|
|
35
|
+
|
|
36
|
+
async def pairs(self) -> dict:
|
|
37
|
+
res = await self._get("api/trade/info")
|
|
38
|
+
return res["success"] and {
|
|
39
|
+
p: {
|
|
40
|
+
k: v for k, v in d.items() if k in ("price_prec", "min_price", "max_price", "amount_prec", "value_prec")
|
|
41
|
+
}
|
|
42
|
+
for p, d in res["pairs"].items()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async def trade(self, pair: str, sell: bool, amount: float, price: float) -> tuple[int, int]:
|
|
46
|
+
data = {
|
|
47
|
+
"pair": pair,
|
|
48
|
+
"type": "limit",
|
|
49
|
+
"action": "sell" if sell else "buy",
|
|
50
|
+
"price": price,
|
|
51
|
+
"amount": amount,
|
|
52
|
+
}
|
|
53
|
+
res = await self._req("order_create", data)
|
|
54
|
+
return res["success"] and (res["order_id"], int(time()) + 60)
|
|
55
|
+
|
|
56
|
+
async def cancel(self, oid: int) -> bool:
|
|
57
|
+
res = await self._req("order_cancel", {"order_id": oid})
|
|
58
|
+
return res["success"]
|