xync-client 0.0.155__py3-none-any.whl → 0.0.162__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 +0 -294
- xync_client/Abc/Agent.py +326 -51
- xync_client/Abc/Ex.py +421 -12
- xync_client/Abc/Order.py +7 -14
- xync_client/Abc/xtype.py +35 -3
- xync_client/Bybit/InAgent.py +18 -447
- xync_client/Bybit/agent.py +531 -431
- xync_client/Bybit/etype/__init__.py +0 -0
- xync_client/Bybit/etype/ad.py +47 -34
- xync_client/Bybit/etype/order.py +34 -49
- xync_client/Bybit/ex.py +20 -46
- xync_client/Bybit/order.py +14 -12
- xync_client/Htx/agent.py +82 -40
- xync_client/Htx/etype/ad.py +22 -5
- xync_client/Htx/etype/order.py +194 -0
- xync_client/Htx/ex.py +16 -16
- xync_client/Mexc/agent.py +196 -13
- xync_client/Mexc/api.py +955 -336
- xync_client/Mexc/etype/ad.py +52 -1
- xync_client/Mexc/etype/order.py +131 -416
- xync_client/Mexc/ex.py +29 -19
- 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 -63
- xync_client/Pms/Payeer/trade.py +58 -0
- xync_client/Pms/Volet/{__init__.py → agent.py} +1 -2
- xync_client/loader.py +1 -0
- {xync_client-0.0.155.dist-info → xync_client-0.0.162.dist-info}/METADATA +2 -1
- {xync_client-0.0.155.dist-info → xync_client-0.0.162.dist-info}/RECORD +33 -29
- xync_client/Pms/Payeer/__init__.py +0 -262
- xync_client/Pms/Payeer/api.py +0 -25
- {xync_client-0.0.155.dist-info → xync_client-0.0.162.dist-info}/WHEEL +0 -0
- {xync_client-0.0.155.dist-info → xync_client-0.0.162.dist-info}/top_level.txt +0 -0
xync_client/Mexc/ex.py
CHANGED
|
@@ -6,7 +6,7 @@ import requests
|
|
|
6
6
|
from xync_client.loader import TORM, NET_TOKEN
|
|
7
7
|
|
|
8
8
|
from xync_client.Abc.Ex import BaseExClient
|
|
9
|
-
from xync_client.Abc.xtype import PmEx, MapOfIdsList
|
|
9
|
+
from xync_client.Abc.xtype import PmEx, MapOfIdsList, GetAds
|
|
10
10
|
from xync_client.Mexc.etype import pm, ad
|
|
11
11
|
|
|
12
12
|
from xync_schema import xtype
|
|
@@ -66,26 +66,36 @@ class ExClient(BaseExClient):
|
|
|
66
66
|
p = {cur: {c for c in coins} for cur in curs}
|
|
67
67
|
return p, p
|
|
68
68
|
|
|
69
|
+
async def x2e_pm(self, xid: int, cur_id: int = None) -> str: # pmex.exid
|
|
70
|
+
if not self.pm_x2e.get(xid):
|
|
71
|
+
fltr = dict(pm_id=xid, ex=self.ex)
|
|
72
|
+
if cur_id:
|
|
73
|
+
cur = await models.Cur[cur_id]
|
|
74
|
+
pmexs = await models.PmEx.filter(**fltr)
|
|
75
|
+
self.pm_x2e[xid] = [pmex.exid for pmex in pmexs if pmex.name.endswith(f"({cur.ticker})")][0]
|
|
76
|
+
else:
|
|
77
|
+
self.pm_x2e[xid] = (await models.PmEx.get(**fltr)).exid
|
|
78
|
+
return self.pm_x2e[xid]
|
|
79
|
+
|
|
80
|
+
async def x2e_req_ads(self, xreq: GetAds) -> ad.AdsReq:
|
|
81
|
+
coin_id, _ = await self.x2e_coin(xreq.coin_id)
|
|
82
|
+
cur_id, _, __ = await self.x2e_cur(xreq.cur_id)
|
|
83
|
+
ereq = ad.AdsReq(
|
|
84
|
+
coinId=coin_id,
|
|
85
|
+
currency=cur_id,
|
|
86
|
+
tradeType="SELL" if xreq.is_sell else "BUY",
|
|
87
|
+
payMethod=",".join([await self.x2e_pm(pm_id, cur_id=xreq.cur_id) for pm_id in xreq.pm_ids]),
|
|
88
|
+
)
|
|
89
|
+
if xreq.amount:
|
|
90
|
+
ereq.amount = str(xreq.amount)
|
|
91
|
+
# todo: all kwargs
|
|
92
|
+
return ereq
|
|
93
|
+
|
|
69
94
|
# 24: Список объяв по (buy/sell, cur, coin, pm)
|
|
70
|
-
async def
|
|
71
|
-
self,
|
|
95
|
+
async def _ads(
|
|
96
|
+
self, req: ad.AdsReq, lim: int = None, vm_filter: bool = False, **kwargs
|
|
72
97
|
) -> list[ad.Ad]: # {ad.id: ad}
|
|
73
|
-
|
|
74
|
-
"adsType": 1,
|
|
75
|
-
"allowTrade": "false",
|
|
76
|
-
"amount": amount or "",
|
|
77
|
-
"blockTrade": "false",
|
|
78
|
-
"coinId": coin_exid,
|
|
79
|
-
# "countryCode": "",
|
|
80
|
-
"currency": cur_exid,
|
|
81
|
-
"follow": "false",
|
|
82
|
-
"haveTrade": "false",
|
|
83
|
-
"page": 1,
|
|
84
|
-
"payMethod": ",".join(pm_exids) or "",
|
|
85
|
-
"tradeType": "SELL" if is_sell else "BUY",
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
resp = requests.get("https://p2p.mexc.com/api/market", params=params)
|
|
98
|
+
resp = requests.get("https://p2p.mexc.com/api/market", params=req.model_dump())
|
|
89
99
|
return [ad.Ad(**_ad) for _ad in resp.json()["data"]]
|
|
90
100
|
|
|
91
101
|
|
xync_client/Okx/1.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
headers = {
|
|
5
|
+
"authorization": "eyJraWQiOiIxMzYzODYiLCJhbGciOiJFUzI1NiJ9.eyJqdGkiOiJleDExMDE3NjUwMzE1NDY2MjE4NUU3RUJGMUYyRDFFRTE5MW1ka2IiLCJ1aWQiOiI2T3hmUi9aWGkrZkI0YlVUajRNLzh3PT0iLCJzdGEiOjAsIm1pZCI6IjZPeGZSL1pYaStmQjRiVVRqNE0vOHc9PSIsInBpZCI6IlBUeUE4VzA5ekZVSkJHSjZZUk5HWXc9PSIsIm5kZSI6MCwiaWF0IjoxNzY1MjA0NDE4LCJleHAiOjE3NjY0MTQwMTgsImJpZCI6MCwiZG9tIjoid3d3Lm9reC5jb20iLCJlaWQiOjE4LCJpc3MiOiJva2NvaW4iLCJkaWQiOiJ0OFlKbUFXMTNGeWZpMUZhUUhtQVFNSThNZElmYm44NnFPSnpNZFA3VVhhZFFDcFdQSkQ0NWs0ckMrMk1SWjJXIiwiZmlkIjoidDhZSm1BVzEzRnlmaTFGYVFIbUFRTUk4TWRJZmJuODZxT0p6TWRQN1VYYWRRQ3BXUEpENDVrNHJDKzJNUloyVyIsImxpZCI6IjZPeGZSL1pYaStmQjRiVVRqNE0vOHc9PSIsInVmYiI6IlBUeUE4VzA5ekZVSkJHSjZZUk5HWXc9PSIsInVwYiI6ImlCcmEyVmhOb2t5UmloeGlKLzN6RXc9PSIsImt5YyI6Miwia3lpIjoicHFHRVBYWHN6V1ZVNXhRR1NiNURYTGRUcENaS2c1LzZLbjFteGFpclpDbmovbGJBUGxqRlMwbU5qWGxlYXJ6Rjd5ZEYvWU5DR1FXLzV5aTRWQnpUM1E9PSIsImNwayI6ImhCdjNtSEZjb0lETG5TckZ6dEdTTlpMT29aU2s1bUE4SHBQcE9MOFE1TlVYamowc2w5OG9FK2dRWTRsSUdvbm5QUzJwZ0pQckw4ZGNyMlk4N3VEcjYyd0ExK1ZuRmUyUm10a2RCMFYzY3pVVEI2U09sMCtTQjdnQVJvbHdyeHMza1hvVEdua3BvWEVKamdsL2Q0bEN0aUxuUy9ydGRjY1pnZ0hpeEZhclBxWT0iLCJ2ZXIiOjEsImNsdCI6MiwidXVkIjoiV3VoZlhzRHRicG1GNXN3Q25pKzYrWVhUN2FodmJLWnRYZXZlUXNMVHI4az0iLCJzdWIiOiJBODI1QzI4OTI4QzZCRTNFMTlFREFFRUI1ODhEQTVEQiJ9.I1fFTnP7xYaK1s0jCU01NkjyBgk04dBMp7qFahIzmRUn3e34W6mSNDNSWmi-UIhxH2ORbqbhG2ErUSrrsvai3w",
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
response = requests.get("https://www.okx.com/v3/c2c/receiptAccounts", headers=headers)
|
|
9
|
+
|
|
10
|
+
itm = {
|
|
11
|
+
el["type"]: next((field["value"] for field in el.get("fields") if field.get("key") == "accountNo"))
|
|
12
|
+
for el in response.json()["data"]
|
|
13
|
+
}
|
|
14
|
+
print(itm)
|
xync_client/Okx/agent.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from pyro_client.client.file import FileClient
|
|
2
|
+
from xync_bot import XyncBot
|
|
3
|
+
|
|
4
|
+
from xync_client.Abc.Agent import BaseAgentClient
|
|
5
|
+
from asyncio import run
|
|
6
|
+
from x_model import init_db
|
|
7
|
+
from xync_schema.models import Agent, Ex
|
|
8
|
+
|
|
9
|
+
from xync_client.Okx.ex import ExClient
|
|
10
|
+
from xync_client.loader import NET_TOKEN, PAY_TOKEN
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AgentClient(BaseAgentClient):
|
|
14
|
+
async def my_fiats(self):
|
|
15
|
+
response = await self._get("/v3/c2c/receiptAccounts")
|
|
16
|
+
fiats = response["data"]
|
|
17
|
+
return {
|
|
18
|
+
fiat["type"]: field["value"] for fiat in fiats for field in fiat["fields"] if field["key"] == "accountNo"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def main():
|
|
23
|
+
from xync_client.loader import TORM
|
|
24
|
+
|
|
25
|
+
cn = await init_db(TORM)
|
|
26
|
+
ex = await Ex.get(name="Okx")
|
|
27
|
+
agent = await Agent.get(actor__ex=ex).prefetch_related("actor__ex", "actor__person__user__gmail")
|
|
28
|
+
fbot = FileClient(NET_TOKEN)
|
|
29
|
+
ecl: ExClient = ex.client(fbot)
|
|
30
|
+
abot = XyncBot(PAY_TOKEN, cn)
|
|
31
|
+
cl = agent.client(ecl, fbot, abot)
|
|
32
|
+
|
|
33
|
+
_fiats = await cl.my_fiats()
|
|
34
|
+
|
|
35
|
+
await cl.stop()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
run(main())
|
xync_client/Okx/ex.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from asyncio import run
|
|
2
2
|
|
|
3
3
|
from pyro_client.client.file import FileClient
|
|
4
|
-
from
|
|
4
|
+
from xync_client.loader import NET_TOKEN
|
|
5
5
|
from x_model import init_db
|
|
6
6
|
|
|
7
7
|
from xync_client.Abc.Exception import NoPairOnEx
|
|
@@ -20,16 +20,16 @@ class ExClient(BaseExClient):
|
|
|
20
20
|
"quoteCurrency": cur,
|
|
21
21
|
"needField": "false",
|
|
22
22
|
}
|
|
23
|
-
pms = await self._get("/v3/c2c/configs/receipt/templates", params=params)
|
|
23
|
+
pms = await self._get(url="/v3/c2c/configs/receipt/templates", params=params)
|
|
24
24
|
return [pm.PmE(**_pm) for _pm in pms["data"] if _pm["paymentMethod"]]
|
|
25
25
|
|
|
26
26
|
# 19: Список поддерживаемых валют тейкера
|
|
27
|
-
async def curs(self) -> dict[int, xtype.CurEx]: # {cur.exid: cur.ticker}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
# async def curs(self) -> dict[int, xtype.CurEx]: # {cur.exid: cur.ticker}
|
|
28
|
+
# curs = await self._get("/v3/users/common/list/currencies")
|
|
29
|
+
# return {
|
|
30
|
+
# cur["displayName"]: xtype.CurEx(exid=cur["displayName"], ticker=cur["displayName"], scale=cur["precision"])
|
|
31
|
+
# for cur in curs["data"]
|
|
32
|
+
# }
|
|
33
33
|
|
|
34
34
|
# 20: Список платежных методов
|
|
35
35
|
async def pms(self, cur: models.Cur = None) -> dict[int | str, PmEx]: # {pm.exid: pm}
|
|
@@ -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,64 +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(
|
|
32
|
-
options=options,
|
|
33
|
-
headless=False,
|
|
34
|
-
browser_executable_path="/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta",
|
|
35
|
-
)
|
|
36
|
-
wait = WebDriverWait(driver, timeout=10)
|
|
37
|
-
try:
|
|
38
|
-
driver.get("https://payeer.com/en/auth")
|
|
39
|
-
wait.until(EC.invisibility_of_element_located((By.TAG_NAME, "lottie-player")))
|
|
40
|
-
login_link = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "button.button_empty")))
|
|
41
|
-
login_link.click()
|
|
42
|
-
email_field = wait.until(EC.presence_of_element_located((By.NAME, "email")))
|
|
43
|
-
email_field.send_keys(agent.auth.get("email"))
|
|
44
|
-
password_field = wait.until(EC.presence_of_element_located((By.NAME, "password")))
|
|
45
|
-
password_field.send_keys(agent.auth.get("password"))
|
|
46
|
-
login_button = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "login-form__login-btn.step1")))
|
|
47
|
-
login_button.click()
|
|
48
|
-
time.sleep(4)
|
|
49
|
-
try:
|
|
50
|
-
login_button.click()
|
|
51
|
-
except Exception:
|
|
52
|
-
pass
|
|
53
|
-
time.sleep(1)
|
|
54
|
-
if (v := driver.find_elements(By.CLASS_NAME, "form-input-top")) and v[0].text == "Введите проверочный код":
|
|
55
|
-
code = input("Email code: ")
|
|
56
|
-
actions = ActionChains(driver)
|
|
57
|
-
for char in code:
|
|
58
|
-
actions.send_keys(char).perform()
|
|
59
|
-
step2_button = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "login-form__login-btn.step2")))
|
|
60
|
-
step2_button.click()
|
|
61
|
-
agent.state = {"cookies": driver.get_cookies()}
|
|
62
|
-
await agent.save()
|
|
63
|
-
finally:
|
|
64
|
-
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"]
|