xync-client 0.0.143__py3-none-any.whl → 0.0.145__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 xync-client might be problematic. Click here for more details.
- xync_client/Abc/Agent.py +19 -5
- xync_client/Abc/HasAbotUid.py +10 -0
- xync_client/Abc/InAgent.py +5 -3
- xync_client/Abc/PmAgent.py +33 -22
- xync_client/Bybit/InAgent.py +46 -29
- xync_client/Bybit/agent.py +11 -8
- xync_client/Gmail/__init__.py +106 -98
- xync_client/Pms/Payeer/__init__.py +32 -25
- xync_client/Pms/Payeer/login.py +6 -2
- xync_client/Pms/Volet/__init__.py +80 -61
- xync_client/Pms/Volet/api.py +5 -4
- xync_client/loader.py +1 -0
- {xync_client-0.0.143.dist-info → xync_client-0.0.145.dist-info}/METADATA +1 -1
- {xync_client-0.0.143.dist-info → xync_client-0.0.145.dist-info}/RECORD +16 -15
- {xync_client-0.0.143.dist-info → xync_client-0.0.145.dist-info}/WHEEL +0 -0
- {xync_client-0.0.143.dist-info → xync_client-0.0.145.dist-info}/top_level.txt +0 -0
xync_client/Abc/Agent.py
CHANGED
|
@@ -3,8 +3,9 @@ from abc import abstractmethod
|
|
|
3
3
|
from pydantic import BaseModel
|
|
4
4
|
from pyro_client.client.file import FileClient
|
|
5
5
|
from x_client.aiohttp import Client as HttpClient
|
|
6
|
+
from xync_bot import XyncBot
|
|
6
7
|
from xync_schema import models
|
|
7
|
-
from xync_schema.models import OrderStatus, Coin, Cur, Ad, AdStatus, Actor
|
|
8
|
+
from xync_schema.models import OrderStatus, Coin, Cur, Ad, AdStatus, Actor, Agent
|
|
8
9
|
from xync_schema.xtype import BaseAd
|
|
9
10
|
|
|
10
11
|
from xync_client.Abc.Ex import BaseExClient
|
|
@@ -12,10 +13,23 @@ from xync_client.Abc.xtype import CredExOut, BaseOrderReq, BaseAdUpdate
|
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class BaseAgentClient(HttpClient):
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
bbot: XyncBot
|
|
17
|
+
fbot: FileClient
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
agent: Agent,
|
|
22
|
+
fbot: FileClient,
|
|
23
|
+
bbot: XyncBot,
|
|
24
|
+
headers: dict[str, str] = None,
|
|
25
|
+
cookies: dict[str, str] = None,
|
|
26
|
+
):
|
|
27
|
+
self.bbot = bbot
|
|
28
|
+
self.fbot = fbot
|
|
29
|
+
self.agent: Agent = agent
|
|
30
|
+
self.actor: Actor = agent.actor
|
|
31
|
+
super().__init__(self.actor.ex.host_p2p, headers, cookies)
|
|
32
|
+
self.ex_client: BaseExClient = self.actor.ex.client(fbot)
|
|
19
33
|
|
|
20
34
|
# 0: Получшение ордеров в статусе status, по монете coin, в валюте coin, в направлении is_sell: bool
|
|
21
35
|
@abstractmethod
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from PGram import Bot
|
|
2
|
+
from aiogram.types import Message
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class HasAbotUid:
|
|
6
|
+
abot: Bot
|
|
7
|
+
uid: int
|
|
8
|
+
|
|
9
|
+
async def receive(self, text: str, photo: bytes = None, video: bytes = None) -> Message:
|
|
10
|
+
return await self.abot.send(self.uid, txt=text, photo=photo, video=video)
|
xync_client/Abc/InAgent.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
|
|
3
3
|
from pyro_client.client.file import FileClient
|
|
4
|
+
from xync_bot import XyncBot
|
|
5
|
+
|
|
4
6
|
from xync_client.Abc.PmAgent import PmAgentClient
|
|
5
|
-
from xync_schema.models import
|
|
7
|
+
from xync_schema.models import Agent
|
|
6
8
|
|
|
7
9
|
from xync_client.Abc.Agent import BaseAgentClient
|
|
8
10
|
|
|
@@ -10,8 +12,8 @@ from xync_client.Abc.Agent import BaseAgentClient
|
|
|
10
12
|
class BaseInAgentClient:
|
|
11
13
|
pmacs: dict[int, PmAgentClient] = {}
|
|
12
14
|
|
|
13
|
-
def __init__(self,
|
|
14
|
-
self.agent_client: BaseAgentClient =
|
|
15
|
+
def __init__(self, agent: Agent, fbot: FileClient, bbot: XyncBot):
|
|
16
|
+
self.agent_client: BaseAgentClient = agent.client(fbot, bbot)
|
|
15
17
|
|
|
16
18
|
@abstractmethod
|
|
17
19
|
async def start_listen(self) -> bool: ...
|
xync_client/Abc/PmAgent.py
CHANGED
|
@@ -4,52 +4,57 @@ from datetime import datetime, timedelta
|
|
|
4
4
|
from decimal import Decimal
|
|
5
5
|
from enum import StrEnum
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from PGram import Bot
|
|
8
|
+
from playwright.async_api import Page, Playwright, Browser
|
|
8
9
|
from pyro_client.client.file import FileClient
|
|
9
10
|
from pyro_client.client.user import UserClient
|
|
11
|
+
from tortoise.timezone import now
|
|
10
12
|
|
|
11
|
-
from xync_client.loader import NET_TOKEN
|
|
12
13
|
from xync_schema.enums import UserStatus
|
|
13
|
-
from xync_schema.models import PmAgent, User
|
|
14
|
+
from xync_schema.models import PmAgent, User, Transfer
|
|
15
|
+
|
|
16
|
+
from xync_client.Abc.HasAbotUid import HasAbotUid
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class LoginFailedException(Exception): ...
|
|
17
20
|
|
|
18
21
|
|
|
19
|
-
class PmAgentClient(metaclass=ABCMeta):
|
|
22
|
+
class PmAgentClient(HasAbotUid, metaclass=ABCMeta):
|
|
20
23
|
class Pages(StrEnum):
|
|
21
24
|
base = "https://host"
|
|
22
25
|
LOGIN = base + "login"
|
|
23
26
|
SEND = base + "send"
|
|
24
27
|
OTP_LOGIN = base + "login/otp"
|
|
25
28
|
|
|
29
|
+
browser: Browser
|
|
26
30
|
norm: str
|
|
27
31
|
agent: PmAgent
|
|
28
|
-
|
|
32
|
+
ubot: FileClient | UserClient = None
|
|
29
33
|
page: Page
|
|
30
34
|
pages: type(StrEnum) = Pages
|
|
31
|
-
last_page: int = 0
|
|
32
35
|
last_active: datetime = datetime.now()
|
|
36
|
+
with_userbot: bool = False
|
|
33
37
|
_is_started: bool = False
|
|
34
38
|
|
|
35
|
-
async def start(self, pw: Playwright, headed: bool = False
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
async def start(self, pw: Playwright, headed: bool = False) -> "PmAgentClient":
|
|
40
|
+
if self.with_userbot:
|
|
41
|
+
self.ubot = UserClient(self.uid)
|
|
42
|
+
await self.ubot.start()
|
|
39
43
|
|
|
40
44
|
self.browser = await pw.chromium.launch(
|
|
41
|
-
channel="
|
|
45
|
+
channel="chrome-beta" if headed else "chromium-headless-shell", headless=not headed
|
|
42
46
|
)
|
|
47
|
+
# noinspection PyTypeChecker
|
|
43
48
|
context = await self.browser.new_context(storage_state=self.agent.state)
|
|
44
49
|
self.page = await context.new_page()
|
|
45
|
-
await self.page.goto(self.pages.SEND) # Оптимистично переходим сразу на страницу отправки
|
|
50
|
+
await self.page.goto(self.pages.SEND, wait_until="commit") # Оптимистично переходим сразу на страницу отправки
|
|
46
51
|
if self.page.url.startswith(self.pages.LOGIN): # Если перебросило на страницу логина
|
|
47
52
|
await self._login() # Логинимся
|
|
48
53
|
if not self.page.url.startswith(self.pages.SEND): # Если в итоге не удалось попасть на отправку
|
|
49
|
-
await self.
|
|
54
|
+
await self.receive(self.norm + " not logged in!", photo=await self.page.screenshot())
|
|
50
55
|
raise LoginFailedException(f"User {self.agent.user_id} has not logged in")
|
|
51
56
|
loop = get_running_loop()
|
|
52
|
-
self.last_active =
|
|
57
|
+
self.last_active = now()
|
|
53
58
|
loop.create_task(self._idle()) # Бесконечно пасёмся в фоне на странице отправки, что бы куки не протухли
|
|
54
59
|
self._is_started = True
|
|
55
60
|
return self
|
|
@@ -59,18 +64,21 @@ class PmAgentClient(metaclass=ABCMeta):
|
|
|
59
64
|
async def _idle(self): # todo: не мешать другим процессам, обновлять на другой вкладке?
|
|
60
65
|
while (await User.get(username_id=self.uid)).status >= UserStatus.ACTIVE:
|
|
61
66
|
await self.page.wait_for_timeout(30 * 1000)
|
|
62
|
-
if self.last_active <
|
|
63
|
-
await self.page.reload()
|
|
64
|
-
self.last_active =
|
|
65
|
-
await self.
|
|
67
|
+
if self.last_active < now() - timedelta(minutes=1):
|
|
68
|
+
await self.page.reload(wait_until="commit")
|
|
69
|
+
self.last_active = now()
|
|
70
|
+
await self.receive(self.norm + " stoped")
|
|
66
71
|
await self.stop()
|
|
67
72
|
|
|
68
73
|
async def stop(self):
|
|
69
74
|
# save state
|
|
75
|
+
# noinspection PyTypeChecker
|
|
70
76
|
self.agent.state = await self.page.context.storage_state()
|
|
71
77
|
await self.agent.save()
|
|
72
78
|
# closing
|
|
73
|
-
await self.
|
|
79
|
+
await self.abot.stop()
|
|
80
|
+
if self.ubot:
|
|
81
|
+
await self.ubot.stop()
|
|
74
82
|
await self.page.context.close()
|
|
75
83
|
await self.page.context.browser.close()
|
|
76
84
|
self._is_started = False
|
|
@@ -79,15 +87,18 @@ class PmAgentClient(metaclass=ABCMeta):
|
|
|
79
87
|
async def _login(self): ...
|
|
80
88
|
|
|
81
89
|
@abstractmethod
|
|
82
|
-
async def send(self,
|
|
90
|
+
async def send(self, t: Transfer) -> tuple[int, bytes] | float: ...
|
|
83
91
|
|
|
84
92
|
@abstractmethod # проверка поступления определенной суммы за последние пол часа (минимум), return точную сумму
|
|
85
|
-
async def check_in(
|
|
93
|
+
async def check_in(
|
|
94
|
+
self, amount: int | Decimal | float, cur: str, dt: datetime, tid: str | int = None
|
|
95
|
+
) -> float | None: ...
|
|
86
96
|
|
|
87
97
|
@abstractmethod # видео входа в аккаунт, и переход в историю поступлений за последние сутки (минимум)
|
|
88
98
|
async def proof(self) -> bytes: ...
|
|
89
99
|
|
|
90
|
-
def __init__(self, agent: PmAgent):
|
|
100
|
+
def __init__(self, agent: PmAgent, abot: Bot):
|
|
91
101
|
self.agent = agent
|
|
102
|
+
self.abot = abot
|
|
92
103
|
self.uid = agent.user.username_id
|
|
93
104
|
self.norm = agent.pm.norm
|
xync_client/Bybit/InAgent.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import re
|
|
4
|
+
import traceback
|
|
4
5
|
from datetime import datetime, timezone, timedelta
|
|
5
6
|
from uuid import uuid4
|
|
6
7
|
|
|
@@ -14,6 +15,7 @@ from pyro_client.client.file import FileClient
|
|
|
14
15
|
from tortoise.exceptions import IntegrityError
|
|
15
16
|
from tortoise.timezone import now
|
|
16
17
|
from tortoise.transactions import in_transaction
|
|
18
|
+
from xync_bot import XyncBot
|
|
17
19
|
|
|
18
20
|
from xync_client.Abc.PmAgent import PmAgentClient
|
|
19
21
|
from xync_schema import models
|
|
@@ -28,7 +30,7 @@ from xync_client.Bybit.etype.order import (
|
|
|
28
30
|
OrderFull,
|
|
29
31
|
StatusApi,
|
|
30
32
|
)
|
|
31
|
-
from xync_client.loader import NET_TOKEN
|
|
33
|
+
from xync_client.loader import NET_TOKEN, PAY_TOKEN
|
|
32
34
|
from xync_client.Abc.InAgent import BaseInAgentClient
|
|
33
35
|
from xync_client.Bybit.agent import AgentClient
|
|
34
36
|
|
|
@@ -39,7 +41,7 @@ class InAgentClient(BaseInAgentClient):
|
|
|
39
41
|
async def start_listen(self):
|
|
40
42
|
t = await self.agent_client.ott()
|
|
41
43
|
ts = int(float(t["time_now"]) * 1000)
|
|
42
|
-
await self.ws_prv(self.agent_client.
|
|
44
|
+
await self.ws_prv(self.agent_client.agent.auth["deviceId"], t["result"], ts)
|
|
43
45
|
|
|
44
46
|
# 3N: [T] - Уведомление об одобрении запроса на сделку
|
|
45
47
|
async def request_accepted_notify(self) -> int: ... # id
|
|
@@ -96,11 +98,11 @@ class InAgentClient(BaseInAgentClient):
|
|
|
96
98
|
# сразу уменьшаем доступный остаток монеты/валюты
|
|
97
99
|
await self.money_upd(order_db)
|
|
98
100
|
if upd.side: # я покупатель - ждем мою оплату
|
|
99
|
-
|
|
100
|
-
if not re.match(r"^([PpРр])\d{7,10}\b",
|
|
101
|
+
_dest = order.paymentTermList[0].accountNo
|
|
102
|
+
if not re.match(r"^([PpРр])\d{7,10}\b", _dest):
|
|
101
103
|
continue
|
|
102
104
|
await order_db.fetch_related("ad__pair_side__pair", "cred__pmcur__cur")
|
|
103
|
-
await self.send_payment(order_db
|
|
105
|
+
await self.send_payment(order_db)
|
|
104
106
|
case StatusApi.wait_for_buyer:
|
|
105
107
|
if upd.side == 0: # ждем когда покупатель оплатит
|
|
106
108
|
if not (pmacdx := await self.get_pma_by_cdex(order)):
|
|
@@ -140,9 +142,9 @@ class InAgentClient(BaseInAgentClient):
|
|
|
140
142
|
if (
|
|
141
143
|
o["amount"] == order.amount
|
|
142
144
|
and o["id"] != upd.id
|
|
143
|
-
and o
|
|
144
|
-
|
|
145
|
-
and
|
|
145
|
+
and int(order.createDate) < int(o["createDate"]) + 900 * 000
|
|
146
|
+
# todo: get full_order from o, and cred or pm from full_order:
|
|
147
|
+
# and o['paymentTermList'][0].accountNo == order.paymentTermList[0].accountNo
|
|
146
148
|
)
|
|
147
149
|
]
|
|
148
150
|
curex = await models.CurEx.get(
|
|
@@ -245,7 +247,10 @@ class InAgentClient(BaseInAgentClient):
|
|
|
245
247
|
if not upd.message:
|
|
246
248
|
...
|
|
247
249
|
if im_buyer and (g := re.match(r"^[PpРр]\d{7,10}\b", upd.message)):
|
|
248
|
-
|
|
250
|
+
if not order_db.cred.detail.startswith(dest := g.group()):
|
|
251
|
+
order_db.cred.detail = dest
|
|
252
|
+
await order_db.save()
|
|
253
|
+
await self.send_payment(order_db)
|
|
249
254
|
case "READ":
|
|
250
255
|
upd = Read.model_validate(data["data"])
|
|
251
256
|
# if upd.status not in (StatusWs.created, StatusWs.canceled, 10, StatusWs.completed):
|
|
@@ -318,7 +323,7 @@ class InAgentClient(BaseInAgentClient):
|
|
|
318
323
|
await fiat.save(update_fields=["amount"])
|
|
319
324
|
logging.info(f"Order #{order_db.id} {order_db.status.name}. Fiat: {fiat.amount}, Asset: {ass.free}")
|
|
320
325
|
|
|
321
|
-
async def send_payment(self, order_db: models.Order
|
|
326
|
+
async def send_payment(self, order_db: models.Order):
|
|
322
327
|
if order_db.status != OrderStatus.created:
|
|
323
328
|
return
|
|
324
329
|
fmt_am = round(order_db.amount * 10**-2, 2)
|
|
@@ -335,19 +340,23 @@ class InAgentClient(BaseInAgentClient):
|
|
|
335
340
|
# проверяем не отправляли ли мы уже перевод по этому ордеру
|
|
336
341
|
if t := await models.Transfer.get_or_none(order=order_db, amount=order_db.amount):
|
|
337
342
|
await pma.bot.send(
|
|
338
|
-
f"Order# {order_db.exid}: Double send {fmt_am}{cur} to {
|
|
343
|
+
f"Order# {order_db.exid}: Double send {fmt_am}{cur} to {order_db.cred.detail} #{t.pmid}!",
|
|
339
344
|
self.agent_client.actor.person.user.username_id,
|
|
340
345
|
)
|
|
341
|
-
raise Exception(
|
|
346
|
+
raise Exception(
|
|
347
|
+
f"Order# {order_db.exid}: Double send {fmt_am}{cur} to {order_db.cred.detail} #{t.pmid}!"
|
|
348
|
+
)
|
|
342
349
|
|
|
343
350
|
# ставим в бд статус "оплачен"
|
|
344
351
|
order_db.status = OrderStatus.paid
|
|
345
352
|
order_db.payed_at = datetime.now(timezone.utc)
|
|
346
353
|
await order_db.save()
|
|
347
|
-
# отправляем деньги
|
|
348
|
-
tid, img, rest_amount = await pma.send(dest=dest, amount=fmt_am, cur=cur)
|
|
349
354
|
# создаем перевод в бд
|
|
350
|
-
t
|
|
355
|
+
t = models.Transfer(order=order_db, amount=order_db.amount, updated_at=now())
|
|
356
|
+
# отправляем деньги
|
|
357
|
+
tid, img = await pma.send(t)
|
|
358
|
+
t.pmid = tid
|
|
359
|
+
await t.save()
|
|
351
360
|
await self.send_receipt(str(order_db.exid), tid) # отправляем продавцу чек
|
|
352
361
|
logging.info(f"Order {order_db.exid} PAID at {datetime.now()}: {fmt_am}!")
|
|
353
362
|
|
|
@@ -396,33 +405,41 @@ async def main():
|
|
|
396
405
|
from x_model import init_db
|
|
397
406
|
from xync_client.loader import TORM
|
|
398
407
|
|
|
399
|
-
|
|
408
|
+
cn = await init_db(TORM, True)
|
|
400
409
|
logging.basicConfig(level=logging.INFO)
|
|
401
410
|
|
|
402
|
-
|
|
403
|
-
await models.
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
411
|
+
agent = (
|
|
412
|
+
await models.Agent.filter(
|
|
413
|
+
actor__ex_id=4,
|
|
414
|
+
active=True,
|
|
415
|
+
auth__isnull=False,
|
|
416
|
+
actor__person__user__status=UserStatus.ACTIVE,
|
|
417
|
+
actor__person__user__pm_agents__isnull=False,
|
|
408
418
|
)
|
|
409
|
-
.prefetch_related("
|
|
419
|
+
.prefetch_related("actor__ex", "actor__person__user")
|
|
410
420
|
.first()
|
|
411
421
|
)
|
|
422
|
+
pm_agents = await models.PmAgent.filter(
|
|
423
|
+
active=True,
|
|
424
|
+
auth__isnull=False,
|
|
425
|
+
user__status=UserStatus.ACTIVE,
|
|
426
|
+
).prefetch_related("pm", "user__gmail")
|
|
427
|
+
|
|
428
|
+
bbot = XyncBot(PAY_TOKEN, cn)
|
|
412
429
|
|
|
413
430
|
async with FileClient(NET_TOKEN) as b:
|
|
414
431
|
b: FileClient
|
|
415
|
-
cl: InAgentClient =
|
|
432
|
+
cl: InAgentClient = agent.in_client(b, bbot)
|
|
416
433
|
# await cl.agent_client.export_my_ads()
|
|
417
434
|
# payeer_cl = Client(actor.person.user.username_id)
|
|
418
|
-
for pma in
|
|
419
|
-
pcl = pma.client()
|
|
420
|
-
cl.pmacs[pma.pm_id] = await pcl.start(await async_playwright().start(),
|
|
435
|
+
for pma in pm_agents:
|
|
436
|
+
pcl: PmAgentClient = pma.client(bbot)
|
|
437
|
+
cl.pmacs[pma.pm_id] = await pcl.start(await async_playwright().start(), True)
|
|
421
438
|
try:
|
|
422
439
|
_ = await cl.start_listen()
|
|
423
440
|
except Exception as e:
|
|
424
|
-
await b.send("😱Bybit InAgent CRASHED!!!😱", actor.person.user.username_id)
|
|
425
|
-
await b.send(
|
|
441
|
+
await b.send("😱Bybit InAgent CRASHED!!!😱", agent.actor.person.user.username_id)
|
|
442
|
+
await b.send(f"```\n{''.join(traceback.format_exception(e))}\n```", agent.actor.person.user.username_id)
|
|
426
443
|
await cl.agent_client.close()
|
|
427
444
|
|
|
428
445
|
|
xync_client/Bybit/agent.py
CHANGED
|
@@ -24,10 +24,11 @@ from tortoise.signals import post_save
|
|
|
24
24
|
from urllib3.exceptions import ReadTimeoutError
|
|
25
25
|
from x_model import init_db
|
|
26
26
|
from x_model.func import ArrayAgg
|
|
27
|
+
from xync_bot import XyncBot
|
|
27
28
|
from xync_schema import models
|
|
28
29
|
from xync_schema.enums import OrderStatus
|
|
29
30
|
|
|
30
|
-
from xync_schema.models import Actor, Cond, CondSim, PmCur, PairSide
|
|
31
|
+
from xync_schema.models import Actor, Cond, CondSim, PmCur, PairSide, Agent
|
|
31
32
|
|
|
32
33
|
from xync_client.Abc.Agent import BaseAgentClient
|
|
33
34
|
from xync_client.Abc.xtype import BaseOrderReq, FlatDict
|
|
@@ -43,7 +44,7 @@ from xync_client.Bybit.etype.order import (
|
|
|
43
44
|
Message,
|
|
44
45
|
Status,
|
|
45
46
|
)
|
|
46
|
-
from xync_client.loader import TORM, NET_TOKEN
|
|
47
|
+
from xync_client.loader import TORM, NET_TOKEN, PAY_TOKEN
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
class NoMakerException(Exception):
|
|
@@ -87,9 +88,9 @@ class AgentClient(BaseAgentClient): # Bybit client
|
|
|
87
88
|
rcond_sims: dict[int, set[int]] = defaultdict(set) # backward
|
|
88
89
|
tree: dict = {}
|
|
89
90
|
|
|
90
|
-
def __init__(self,
|
|
91
|
-
super().__init__(
|
|
92
|
-
self.api = P2P(testnet=False, api_key=
|
|
91
|
+
def __init__(self, agent: Agent, fbot: FileClient, bbot: XyncBot, **kwargs):
|
|
92
|
+
super().__init__(agent, fbot, bbot, **kwargs)
|
|
93
|
+
self.api = P2P(testnet=False, api_key=agent.auth["key"], api_secret=agent.auth["sec"])
|
|
93
94
|
self.hist: dict = None
|
|
94
95
|
self.completed_orders: list[int] = None
|
|
95
96
|
|
|
@@ -1248,7 +1249,7 @@ class ExcCode(IntEnum):
|
|
|
1248
1249
|
|
|
1249
1250
|
async def main():
|
|
1250
1251
|
logging.basicConfig(level=logging.INFO)
|
|
1251
|
-
|
|
1252
|
+
cn = await init_db(TORM)
|
|
1252
1253
|
|
|
1253
1254
|
@post_save(models.Race)
|
|
1254
1255
|
async def race_upserted(
|
|
@@ -1262,12 +1263,14 @@ async def main():
|
|
|
1262
1263
|
...
|
|
1263
1264
|
|
|
1264
1265
|
actor = (
|
|
1265
|
-
await models.
|
|
1266
|
+
await models.Agent.filter(actor__ex_id=4, auth__isnull=False, active=True)
|
|
1267
|
+
.prefetch_related("actor__ex", "actor__person__user")
|
|
1268
|
+
.first()
|
|
1266
1269
|
)
|
|
1267
1270
|
filebot = FileClient(NET_TOKEN)
|
|
1268
1271
|
await filebot.start()
|
|
1269
1272
|
# b.add_handler(MessageHandler(cond_start_handler, command("cond")))
|
|
1270
|
-
cl: AgentClient = actor.client(filebot)
|
|
1273
|
+
cl: AgentClient = actor.client(filebot, XyncBot(PAY_TOKEN, cn))
|
|
1271
1274
|
|
|
1272
1275
|
# await cl.ex_client.set_pairs()
|
|
1273
1276
|
# await cl.ex_client.set_pms()
|
xync_client/Gmail/__init__.py
CHANGED
|
@@ -1,113 +1,121 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import logging
|
|
2
|
+
import pickle
|
|
3
|
+
import re
|
|
4
|
+
from base64 import urlsafe_b64decode
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
from google.auth.transport.requests import Request
|
|
8
|
+
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
9
|
+
from googleapiclient.discovery import Resource, build
|
|
10
|
+
from requests import get
|
|
4
11
|
from xync_schema.models import User, Gmail
|
|
5
12
|
|
|
6
|
-
from xync_client.
|
|
13
|
+
from xync_client.Abc.HasAbotUid import HasAbotUid
|
|
14
|
+
from xync_client.loader import TORM
|
|
15
|
+
|
|
16
|
+
# Область доступа
|
|
17
|
+
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GmClient(HasAbotUid):
|
|
21
|
+
service: Resource
|
|
22
|
+
|
|
23
|
+
def __init__(self, user: User):
|
|
24
|
+
"""Авторизация и создание сервиса Gmail API"""
|
|
25
|
+
creds = None
|
|
26
|
+
# Файл token.pickle хранит токены доступа пользователя
|
|
27
|
+
if user.gmail.token:
|
|
28
|
+
creds = pickle.loads(user.gmail.token)
|
|
7
29
|
|
|
30
|
+
# Если нет валидных credentials, запрашиваем авторизацию
|
|
31
|
+
if not creds or not creds.valid:
|
|
32
|
+
if creds and creds.expired and creds.refresh_token:
|
|
33
|
+
creds.refresh(Request())
|
|
34
|
+
else:
|
|
35
|
+
flow = InstalledAppFlow.from_client_config(user.gmail.auth, SCOPES)
|
|
36
|
+
creds = flow.run_local_server(port=0)
|
|
8
37
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
user: User
|
|
12
|
-
page: Page
|
|
13
|
-
bot: UserClient = None
|
|
14
|
-
HOME = "https://mail.google.com/mail/u/0/"
|
|
38
|
+
# Сохраняем credentials для следующего запуска
|
|
39
|
+
user.gmail.token = pickle.dumps(creds)
|
|
15
40
|
|
|
16
|
-
|
|
17
|
-
self.uid =
|
|
41
|
+
self.service = build("gmail", "v1", credentials=creds)
|
|
42
|
+
self.uid = user.username_id
|
|
18
43
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
44
|
+
def _get_last_email(self, sender_email, subject_keyword=None):
|
|
45
|
+
"""
|
|
46
|
+
Получить последнее письмо от определенного отправителя
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
sender_email: email отправителя (например, 'example@gmail.com')
|
|
50
|
+
subject_keyword: ключевое слово в теме (опционально)
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def _get_email_body(payload):
|
|
54
|
+
"""Извлечь текст письма из payload"""
|
|
55
|
+
if "body" in payload and "data" in payload["body"]:
|
|
56
|
+
return urlsafe_b64decode(payload["body"]["data"]).decode("utf-8")
|
|
57
|
+
return ""
|
|
58
|
+
|
|
59
|
+
# Формируем поисковый запрос
|
|
60
|
+
query = f"from:{sender_email}"
|
|
61
|
+
if subject_keyword:
|
|
62
|
+
query += f" subject:{subject_keyword}"
|
|
63
|
+
|
|
64
|
+
# Ищем письма с этим запросом
|
|
65
|
+
results = (
|
|
66
|
+
self.service.users()
|
|
67
|
+
.messages()
|
|
68
|
+
.list(
|
|
69
|
+
userId="me",
|
|
70
|
+
q=query,
|
|
71
|
+
maxResults=1, # Только последнее письмо
|
|
24
72
|
)
|
|
25
|
-
|
|
26
|
-
channel="chrome" if headed else "chromium-headless-shell",
|
|
27
|
-
headless=not headed,
|
|
28
|
-
args=[
|
|
29
|
-
"--disable-blink-features=AutomationControlled",
|
|
30
|
-
"--no-sandbox",
|
|
31
|
-
"--disable-web-security",
|
|
32
|
-
"--disable-infobars",
|
|
33
|
-
"--disable-extensions",
|
|
34
|
-
"--start-maximized",
|
|
35
|
-
],
|
|
73
|
+
.execute()
|
|
36
74
|
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
tab = self.page.get_by_role("heading").get_by_label(labs[lang]).last
|
|
73
|
-
await tab.click()
|
|
74
|
-
rows = self.page.locator("tbody>>nth=4 >> tr")
|
|
75
|
-
row = rows.get_by_text("Volet.com").and_(rows.get_by_text("Please Confirm Withdrawal"))
|
|
76
|
-
if not await row.count():
|
|
77
|
-
await self.bot.receive("А нет запросов от волета", photo=await self.page.screenshot())
|
|
78
|
-
|
|
79
|
-
await row.click()
|
|
80
|
-
await self.page.wait_for_load_state()
|
|
81
|
-
btn = self.page.locator('a[href^="https://account.volet.com/verify/"]', has_text="confirm").first
|
|
82
|
-
await btn.click()
|
|
83
|
-
|
|
84
|
-
async def load_bot(self):
|
|
85
|
-
if not self.bot:
|
|
86
|
-
bot = FileClient(NET_TOKEN)
|
|
87
|
-
self.bot = UserClient(self.uid, bot)
|
|
88
|
-
if not self.bot.is_connected:
|
|
89
|
-
await self.bot.start()
|
|
90
|
-
|
|
91
|
-
async def stop(self):
|
|
92
|
-
if self.bot and self.bot.is_connected: # todo: do not stop if
|
|
93
|
-
await self.bot.stop(False)
|
|
94
|
-
await self.page.context.close()
|
|
95
|
-
await self.page.context.browser.close()
|
|
75
|
+
|
|
76
|
+
if not (messages := results.get("messages", [])):
|
|
77
|
+
logging.warning("Письма не найдены")
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
# Получаем полную информацию о письме
|
|
81
|
+
message_id = messages[0]["id"]
|
|
82
|
+
message = self.service.users().messages().get(userId="me", id=message_id, format="full").execute()
|
|
83
|
+
|
|
84
|
+
# Извлекаем заголовки
|
|
85
|
+
headers = message["payload"]["headers"]
|
|
86
|
+
subject = next((h["value"] for h in headers if h["name"] == "Subject"), "Нет темы")
|
|
87
|
+
from_email = next((h["value"] for h in headers if h["name"] == "From"), "Неизвестно")
|
|
88
|
+
date = next((h["value"] for h in headers if h["name"] == "Date"), "Неизвестно")
|
|
89
|
+
|
|
90
|
+
# Извлекаем текст письма
|
|
91
|
+
body = _get_email_body(message["payload"])
|
|
92
|
+
|
|
93
|
+
return {"id": message_id, "subject": subject, "from": from_email, "date": date, "body": body}
|
|
94
|
+
|
|
95
|
+
async def mail_confirm(self, amount: float, dt: datetime):
|
|
96
|
+
if email := self._get_last_email("Volet.com", "Please Confirm Withdrawal"):
|
|
97
|
+
date = datetime.strptime(email["date"].split(",")[1].split(" +")[0], "%d %b %Y %H:%M:%S")
|
|
98
|
+
if match := re.search(r"Amount: <b>([\d.]+) [A-Z]{3}</b>", email["body"]):
|
|
99
|
+
amt = float(match.group(1))
|
|
100
|
+
if match := re.search(r"https://account\.volet\.com/verify/([a-f0-9-]+)", email["body"]):
|
|
101
|
+
token = match.group(1)
|
|
102
|
+
|
|
103
|
+
if email and amount == amt and date > dt and token:
|
|
104
|
+
get(f"https://account.volet.com/verify/{token}")
|
|
105
|
+
return True
|
|
106
|
+
|
|
107
|
+
await self.receive("А нет запросов от волета")
|
|
108
|
+
return False
|
|
96
109
|
|
|
97
110
|
|
|
98
111
|
async def _test():
|
|
99
112
|
from x_model import init_db
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
gmc = GmClient(
|
|
105
|
-
|
|
106
|
-
await gmc.start(True)
|
|
107
|
-
except TimeoutError as te:
|
|
108
|
-
raise te
|
|
109
|
-
finally:
|
|
110
|
-
await gmc.stop()
|
|
113
|
+
|
|
114
|
+
_ = await init_db(TORM)
|
|
115
|
+
|
|
116
|
+
gm = await Gmail.get(id=1).prefetch_related("user__username")
|
|
117
|
+
gmc = GmClient(gm)
|
|
118
|
+
await gmc.mail_confirm(amount=90, dt=datetime.now())
|
|
111
119
|
|
|
112
120
|
|
|
113
121
|
if __name__ == "__main__":
|
|
@@ -11,6 +11,7 @@ from os import urandom
|
|
|
11
11
|
from time import sleep
|
|
12
12
|
from urllib.parse import urlencode
|
|
13
13
|
|
|
14
|
+
from PGram import Bot
|
|
14
15
|
from asyncpg.pgproto.pgproto import timedelta
|
|
15
16
|
from cryptography.hazmat.primitives import padding
|
|
16
17
|
from cryptography.hazmat.primitives.ciphers import Cipher
|
|
@@ -18,10 +19,13 @@ from cryptography.hazmat.primitives.ciphers.algorithms import AES
|
|
|
18
19
|
from cryptography.hazmat.primitives.ciphers.modes import CBC
|
|
19
20
|
from payeer_api import PayeerAPI
|
|
20
21
|
from playwright.async_api import async_playwright, Playwright, Error
|
|
22
|
+
|
|
23
|
+
# noinspection PyProtectedMember
|
|
21
24
|
from playwright._impl._errors import TimeoutError
|
|
22
|
-
from
|
|
25
|
+
from xync_bot import XyncBot
|
|
26
|
+
from xync_schema.models import TopUp, TopUpAble, PmAgent, Transfer
|
|
23
27
|
|
|
24
|
-
from xync_client.loader import TORM
|
|
28
|
+
from xync_client.loader import TORM, PAY_TOKEN
|
|
25
29
|
|
|
26
30
|
from xync_client.Abc.PmAgent import PmAgentClient
|
|
27
31
|
from xync_client.Pms.Payeer.login import login
|
|
@@ -64,9 +68,10 @@ class Client(PmAgentClient):
|
|
|
64
68
|
norm: str = "payeer"
|
|
65
69
|
pages: type(StrEnum) = Pages
|
|
66
70
|
api: PayeerAPI
|
|
71
|
+
with_userbot: bool = False
|
|
67
72
|
|
|
68
|
-
def __init__(self, agent: PmAgent):
|
|
69
|
-
super().__init__(agent)
|
|
73
|
+
def __init__(self, agent: PmAgent, abot: XyncBot):
|
|
74
|
+
super().__init__(agent, abot)
|
|
70
75
|
if api_id := self.agent.auth.get("api_id"):
|
|
71
76
|
self.api = PayeerAPI(self.agent.auth["email"], api_id, self.agent.auth["api_sec"])
|
|
72
77
|
|
|
@@ -74,7 +79,7 @@ class Client(PmAgentClient):
|
|
|
74
79
|
await login(self.agent)
|
|
75
80
|
for cookie in self.agent.state["cookies"]:
|
|
76
81
|
await self.page.context.add_cookies([cookie])
|
|
77
|
-
await self.page.goto(self.pages.SEND)
|
|
82
|
+
await self.page.goto(self.pages.SEND, wait_until="commit")
|
|
78
83
|
|
|
79
84
|
@staticmethod
|
|
80
85
|
def form_redirect(topup: TopUp) -> tuple[str, dict | None]:
|
|
@@ -131,20 +136,22 @@ class Client(PmAgentClient):
|
|
|
131
136
|
"ts": datetime.strptime(ti["dateCreate"], "%d.%m.%Y %H:%M:%S") - timedelta(hours=3),
|
|
132
137
|
}
|
|
133
138
|
|
|
134
|
-
async def send(self,
|
|
139
|
+
async def send(self, t: Transfer) -> tuple[str, bytes] | float:
|
|
140
|
+
dest, cur = t.order.cred.detail, t.order.cred.pmcur.cur.ticker
|
|
141
|
+
amount = round(t.order.amount * 10**-t.order.cred.pmcur.cur.scale, t.order.cred.pmcur.cur.scale)
|
|
135
142
|
self.last_active = datetime.now()
|
|
136
143
|
page = self.page
|
|
137
144
|
if not page.url.startswith(self.pages.SEND):
|
|
138
145
|
try:
|
|
139
|
-
await page.goto(self.pages.SEND)
|
|
146
|
+
await page.goto(self.pages.SEND, wait_until="commit")
|
|
140
147
|
except (TimeoutError, Error):
|
|
141
148
|
await login(self.agent)
|
|
142
149
|
for cookie in self.agent.state["cookies"]:
|
|
143
150
|
await page.context.add_cookies([cookie])
|
|
144
151
|
sleep(0.5)
|
|
145
|
-
await page.goto("https://payeer.com/en/account/send/")
|
|
152
|
+
await page.goto("https://payeer.com/en/account/send/", wait_until="commit")
|
|
146
153
|
has_amount = float(self.api.get_balance()[cur]["DOSTUPNO"])
|
|
147
|
-
if
|
|
154
|
+
if amount <= has_amount:
|
|
148
155
|
sleep(0.1)
|
|
149
156
|
await page.locator('input[name="param_ACCOUNT_NUMBER"]').fill(dest)
|
|
150
157
|
await page.locator("select[name=curr_receive]").select_option(value=cur)
|
|
@@ -166,42 +173,41 @@ class Client(PmAgentClient):
|
|
|
166
173
|
except TimeoutError as _:
|
|
167
174
|
logging.error("Repeat!")
|
|
168
175
|
sleep(0.5)
|
|
169
|
-
return await self.send(
|
|
176
|
+
return await self.send(t)
|
|
170
177
|
if await page.locator('.note_txt:has-text("successfully completed")').count():
|
|
171
178
|
transaction = await page.locator(".note_txt").all_text_contents()
|
|
172
|
-
trans_num =
|
|
173
|
-
await page.goto("https://payeer.com/ru/account/history/")
|
|
179
|
+
trans_num = transaction[0].replace("Transaction #", "").split()[0]
|
|
180
|
+
await page.goto("https://payeer.com/ru/account/history/", wait_until="commit")
|
|
174
181
|
await page.click(f".history-id-{trans_num} a.link")
|
|
175
182
|
sleep(1)
|
|
176
183
|
receipt = await page.query_selector(".ui-dialog.ui-corner-all")
|
|
177
|
-
return trans_num, await receipt.screenshot(path=f"tmp/{trans_num}.png")
|
|
184
|
+
return trans_num, await receipt.screenshot(path=f"tmp/{trans_num}.png")
|
|
178
185
|
else:
|
|
179
|
-
await self.
|
|
186
|
+
await self.receive("Payeer хз", photo=await self.page.screenshot())
|
|
180
187
|
return -1
|
|
181
188
|
else:
|
|
182
|
-
await self.
|
|
189
|
+
await self.receive(
|
|
183
190
|
f"Payeer no have {amount}, only {has_amount}{cur} to {dest}",
|
|
184
|
-
self.uid,
|
|
185
191
|
photo=await self.page.screenshot(),
|
|
186
192
|
)
|
|
187
193
|
return has_amount
|
|
188
194
|
|
|
189
195
|
def check_in(
|
|
190
|
-
self, amount: Decimal | int | float, cur: str,
|
|
196
|
+
self, amount: Decimal | int | float, cur: str, dt: datetime = None, tid: str | int = None
|
|
191
197
|
) -> tuple[Decimal | None, int | None]:
|
|
192
198
|
history = self.api.history(type="incoming", append=tid, count=3)
|
|
193
199
|
if tid:
|
|
194
200
|
return (t := history.get(tid)) and Decimal(t["creditedAmount"])
|
|
195
|
-
|
|
201
|
+
ts: list[dict] = [
|
|
196
202
|
h
|
|
197
203
|
for h in history.values()
|
|
198
204
|
if (
|
|
199
205
|
amount <= Decimal(h["creditedAmount"]) <= ceil(amount)
|
|
200
206
|
and h["creditedCurrency"] == cur
|
|
201
|
-
and datetime.fromisoformat(h["date"]) >
|
|
207
|
+
and datetime.fromisoformat(h["date"]) > dt - timedelta(minutes=1)
|
|
202
208
|
)
|
|
203
209
|
]
|
|
204
|
-
if not (t :=
|
|
210
|
+
if not (t := ts and ts[0]):
|
|
205
211
|
return None, None
|
|
206
212
|
return (
|
|
207
213
|
amount <= (am := Decimal(t["creditedAmount"])) <= ceil(amount) and t["creditedCurrency"] == cur
|
|
@@ -219,7 +225,8 @@ async def main(uid: int):
|
|
|
219
225
|
)
|
|
220
226
|
if not agent:
|
|
221
227
|
raise Exception(f"No active user #{uid} with agent for volet!")
|
|
222
|
-
|
|
228
|
+
abot = Bot(PAY_TOKEN)
|
|
229
|
+
pyr = agent.client(abot)
|
|
223
230
|
playwright: Playwright = await async_playwright().start()
|
|
224
231
|
try:
|
|
225
232
|
dest, amount, cur = "P79619335", 4, "RUB"
|
|
@@ -236,14 +243,14 @@ async def main(uid: int):
|
|
|
236
243
|
res = pyr.check_in(3, cur, datetime.now())
|
|
237
244
|
|
|
238
245
|
if len(res) > 1 and isinstance(res[1], bytes):
|
|
239
|
-
await pyr.
|
|
246
|
+
await pyr.receive(f"Transaction #{res[0]}", photo=res[1])
|
|
240
247
|
elif res[0] > 0:
|
|
241
|
-
await pyr.
|
|
248
|
+
await pyr.receive(f"Sreen of transaction #{res[0]} failed", photo=await pyr.page.screenshot())
|
|
242
249
|
else:
|
|
243
|
-
await pyr.
|
|
250
|
+
await pyr.receive(f"Sending {amount} {cur} to {dest} FAILED", photo=await pyr.page.screenshot())
|
|
244
251
|
|
|
245
252
|
except TimeoutError as te:
|
|
246
|
-
await pyr.
|
|
253
|
+
await pyr.receive(repr(te), photo=await pyr.page.screenshot())
|
|
247
254
|
raise te
|
|
248
255
|
# finally:
|
|
249
256
|
# await pyr.stop()
|
xync_client/Pms/Payeer/login.py
CHANGED
|
@@ -12,7 +12,7 @@ async def login(agent: PmAgent):
|
|
|
12
12
|
options.add_argument("--disable-blink-features=AutomationControlled")
|
|
13
13
|
options.add_argument("--no-sandbox")
|
|
14
14
|
options.add_argument("--disable-dev-shm-usage")
|
|
15
|
-
options.add_argument("--headless=new") # for Chrome >= 109
|
|
15
|
+
# options.add_argument("--headless=new") # for Chrome >= 109
|
|
16
16
|
options.add_argument("--disable-renderer-backgrounding")
|
|
17
17
|
options.add_argument("--disable-background-timer-throttling")
|
|
18
18
|
options.add_argument("--disable-backgrounding-occluded-windows")
|
|
@@ -28,7 +28,11 @@ async def login(agent: PmAgent):
|
|
|
28
28
|
options.add_argument("--window-size=1920,1080")
|
|
29
29
|
options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
|
30
30
|
|
|
31
|
-
driver = uc.Chrome(
|
|
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
|
+
)
|
|
32
36
|
wait = WebDriverWait(driver, timeout=10)
|
|
33
37
|
try:
|
|
34
38
|
driver.get("https://payeer.com/en/auth")
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import re
|
|
3
|
-
from asyncio import run
|
|
3
|
+
from asyncio import run
|
|
4
|
+
from datetime import datetime
|
|
4
5
|
from decimal import Decimal
|
|
5
6
|
from enum import StrEnum
|
|
6
7
|
from hashlib import sha256
|
|
7
8
|
|
|
9
|
+
from PGram import Bot
|
|
8
10
|
from playwright.async_api import async_playwright, Page, Locator, Position, Playwright # , FloatRect
|
|
9
11
|
from pyotp import TOTP
|
|
10
12
|
|
|
@@ -12,12 +14,15 @@ from pyotp import TOTP
|
|
|
12
14
|
from playwright._impl._errors import TimeoutError
|
|
13
15
|
from pyro_client.client.user import UserClient
|
|
14
16
|
from pyrogram.handlers import MessageHandler
|
|
17
|
+
from tortoise.timezone import now
|
|
18
|
+
from xync_schema import models
|
|
15
19
|
from xync_schema.enums import UserStatus
|
|
16
|
-
from xync_schema.models import Cur, User, PmAgent, Cred, PmCur, Fiat, TopUp
|
|
20
|
+
from xync_schema.models import Cur, User, PmAgent, Cred, PmCur, Fiat, TopUp, Transfer
|
|
17
21
|
|
|
18
22
|
from xync_client.Abc.PmAgent import PmAgentClient
|
|
19
23
|
from xync_client.Gmail import GmClient
|
|
20
24
|
from xync_client.Pms.Volet.api import APIClient
|
|
25
|
+
from xync_client.loader import PAY_TOKEN
|
|
21
26
|
|
|
22
27
|
|
|
23
28
|
class CaptchaException(Exception): ...
|
|
@@ -58,24 +63,28 @@ class Client(PmAgentClient):
|
|
|
58
63
|
# HOME = base + "pages/transaction"
|
|
59
64
|
SEND = base + "pages/transfer/wallet"
|
|
60
65
|
|
|
61
|
-
async def check_in(
|
|
62
|
-
|
|
66
|
+
async def check_in(
|
|
67
|
+
self, amount: int | Decimal | float, cur: str, dt: datetime, tid: str | int = None
|
|
68
|
+
) -> float | None:
|
|
69
|
+
return await (self.api.check_by_id(tid) if tid else self.api.check_by_amount(amount, cur))
|
|
63
70
|
|
|
64
71
|
async def proof(self) -> bytes:
|
|
65
72
|
pass
|
|
66
73
|
|
|
67
74
|
uid: int
|
|
68
75
|
agent: PmAgent
|
|
69
|
-
|
|
76
|
+
abot: Bot
|
|
77
|
+
ubot: UserClient
|
|
70
78
|
api: APIClient
|
|
71
79
|
page: Page
|
|
72
80
|
gmail: GmClient
|
|
73
81
|
norm: str = "payeer"
|
|
74
82
|
pages: type(StrEnum) = Pages
|
|
83
|
+
with_userbot: bool = True
|
|
75
84
|
|
|
76
|
-
def __init__(self, agent: PmAgent):
|
|
77
|
-
super().__init__(agent)
|
|
78
|
-
self.gmail = GmClient(
|
|
85
|
+
def __init__(self, agent: PmAgent, abot: Bot):
|
|
86
|
+
super().__init__(agent, abot)
|
|
87
|
+
self.gmail = GmClient(agent.user)
|
|
79
88
|
self.api = APIClient(self.agent.auth["api"], self.agent.auth["password"], self.agent.auth["login"])
|
|
80
89
|
|
|
81
90
|
@staticmethod
|
|
@@ -113,77 +122,92 @@ class Client(PmAgentClient):
|
|
|
113
122
|
"ts": t["updatedTime"],
|
|
114
123
|
}
|
|
115
124
|
|
|
116
|
-
async def start(self, pw: Playwright, headed: bool = False):
|
|
117
|
-
ensure_future(self.gmail.start(pw, False))
|
|
118
|
-
return await super().start(pw, False, True)
|
|
119
|
-
|
|
120
125
|
async def wait_for_code(self, uid: int, topic: str, hg: tuple[MessageHandler, int]) -> str:
|
|
121
|
-
code = await self.
|
|
126
|
+
code = await self.ubot.wait_from(uid, topic, hg)
|
|
122
127
|
return code and code[-6:]
|
|
123
128
|
|
|
124
129
|
async def _login(self):
|
|
125
130
|
ll = self.page.locator("input#j_username")
|
|
126
|
-
await ll.fill(
|
|
127
|
-
await self.page.locator("input#j_password").fill("
|
|
128
|
-
await self.page.wait_for_timeout(
|
|
131
|
+
await ll.fill(self.agent.auth["login"])
|
|
132
|
+
await self.page.locator("input#j_password").fill(self.agent.auth["password"])
|
|
133
|
+
await self.page.wait_for_timeout(300)
|
|
129
134
|
await ll.click()
|
|
135
|
+
await ll.press(key="ArrowLeft")
|
|
136
|
+
await ll.blur()
|
|
130
137
|
volet_bot_id, topic = 243630567, "otp_login"
|
|
131
138
|
await self.page.locator("input#loginToAdvcashButton", has_text="log in").hover()
|
|
132
|
-
hg = self.
|
|
139
|
+
hg = self.ubot.subscribe_for(volet_bot_id, topic)
|
|
133
140
|
await self.page.locator("input#loginToAdvcashButton:not([disabled])", has_text="log in").click()
|
|
134
141
|
await self.page.wait_for_url(self.pages.OTP_LOGIN)
|
|
135
142
|
if not (code := await self.wait_for_code(volet_bot_id, topic, hg)):
|
|
136
|
-
await self.
|
|
143
|
+
await self.ubot.receive("no login code", photo=await self.page.screenshot())
|
|
137
144
|
raise NoCodeException(self.agent.user_id)
|
|
138
145
|
await self.page.locator("input#otpId").fill(code)
|
|
139
146
|
await self.page.click("input#checkOtpButton")
|
|
140
|
-
await self.page.wait_for_url(self.pages.SEND)
|
|
147
|
+
await self.page.wait_for_url(self.pages.SEND, wait_until="domcontentloaded")
|
|
148
|
+
# save state
|
|
149
|
+
# noinspection PyTypeChecker
|
|
150
|
+
self.agent.state = await self.page.context.storage_state()
|
|
151
|
+
await self.agent.save()
|
|
141
152
|
|
|
142
|
-
async def send(self,
|
|
153
|
+
async def send(self, t: Transfer) -> tuple[str, bytes] | float:
|
|
154
|
+
dest, cur = t.order.cred.detail, t.order.cred.pmcur.cur.ticker
|
|
155
|
+
amount = round(t.order.amount * 10**-t.order.cred.pmcur.cur.scale, t.order.cred.pmcur.cur.scale)
|
|
156
|
+
self.last_active = now()
|
|
143
157
|
curs_map = {"RUB": "Ruble"}
|
|
144
|
-
|
|
145
|
-
await self.go(self.pages.SEND)
|
|
158
|
+
await self.go(self.pages.SEND, False)
|
|
146
159
|
await self.page.click("[class=combobox-account]")
|
|
147
|
-
await self.page.click(f'[class=rf-ulst-itm] b:has-text("{curs_map[cur]}")
|
|
148
|
-
await self.page.
|
|
160
|
+
await self.page.click(f'[class=rf-ulst-itm] b:has-text("{curs_map[cur]}")')
|
|
161
|
+
await self.page.wait_for_selector(f"#srcCurrency:has-text('{cur}')")
|
|
149
162
|
await self.page.fill("#srcAmount", str(amount))
|
|
150
|
-
|
|
151
|
-
await
|
|
163
|
+
dw = self.page.locator("#destWalletId")
|
|
164
|
+
await dw.fill(dest)
|
|
165
|
+
await dw.blur()
|
|
166
|
+
await self.page.wait_for_selector(f"#destCurrency:has-text('{cur}')")
|
|
167
|
+
volet_bot_id, topic = 243630567, "otp_send"
|
|
168
|
+
hg = self.ubot.subscribe_for(volet_bot_id, topic)
|
|
152
169
|
await self.page.locator("form#mainForm input[type=submit]", has_text="continue").click()
|
|
153
170
|
# todo: check success confirming
|
|
154
171
|
if otp := self.agent.auth.get("otp"):
|
|
155
172
|
totp = TOTP(otp)
|
|
156
173
|
code = totp.now()
|
|
157
174
|
elif self.agent.user.username.session:
|
|
158
|
-
if not (code := await self.wait_for_code(
|
|
159
|
-
if 1: # todo:
|
|
160
|
-
await self.gmail.mail_confirm()
|
|
161
|
-
|
|
175
|
+
if not (code := await self.wait_for_code(volet_bot_id, topic, hg)):
|
|
176
|
+
if 1: # todo: Is mail_confirm required?
|
|
177
|
+
if _mcr := await self.gmail.mail_confirm(amount, t.updated_at):
|
|
178
|
+
...
|
|
179
|
+
# todo: click Continue
|
|
180
|
+
if not (code := await self.wait_for_code(volet_bot_id, topic, hg)):
|
|
181
|
+
code = await self.wait_for_code(volet_bot_id, topic, hg)
|
|
182
|
+
if not code:
|
|
183
|
+
await self.receive("no send trans code", photo=await self.page.screenshot())
|
|
162
184
|
raise NoCodeException(self.agent.user_id)
|
|
163
185
|
else:
|
|
164
186
|
raise OtpNotSetException(self.agent.user_id)
|
|
165
187
|
await self.page.fill("#securityValue", code)
|
|
166
188
|
await self.page.locator("input[type=submit]", has_text="confirm").click()
|
|
167
189
|
await self.page.wait_for_url(self.pages.SEND)
|
|
190
|
+
tid = await self.page.text_content("ul.p-confirmation-info dl.success>dd")
|
|
168
191
|
await self.page.get_by_role("heading").click()
|
|
169
|
-
slip = await self.page.screenshot(clip={"x": 440, "y": 205, "width":
|
|
170
|
-
await self.
|
|
192
|
+
slip = await self.page.screenshot(clip={"x": 440, "y": 205, "width": 440, "height": 415})
|
|
193
|
+
await self.receive(f"{amount} to {dest} sent", photo=slip)
|
|
194
|
+
return tid, slip
|
|
171
195
|
|
|
172
|
-
async def go(self, url: Pages):
|
|
196
|
+
async def go(self, url: Pages, commit: bool = True):
|
|
173
197
|
try:
|
|
174
|
-
await self.page.goto(url)
|
|
198
|
+
await self.page.goto(url, wait_until="commit" if commit else "domcontentloaded")
|
|
175
199
|
if len(await self.page.content()) < 1000: # todo: fix captcha symptom
|
|
176
200
|
await self.captcha_click()
|
|
177
201
|
except Exception as e:
|
|
178
|
-
await self.
|
|
202
|
+
await self.receive(repr(e), photo=await self.page.screenshot())
|
|
179
203
|
raise e
|
|
180
204
|
|
|
181
205
|
async def send_cap_help(self, xcap: Locator):
|
|
182
206
|
if await xcap.count():
|
|
183
207
|
bb = await xcap.bounding_box(timeout=2000)
|
|
184
208
|
byts = await self.page.screenshot(clip=bb)
|
|
185
|
-
await self.
|
|
186
|
-
txt = await self.
|
|
209
|
+
await self.receive("put x, y", photo=byts)
|
|
210
|
+
txt = await self.ubot.wait_from(self.uid, "xy", timeout=59) # todo: fix
|
|
187
211
|
for xy in txt.split(";"):
|
|
188
212
|
px, py = xy
|
|
189
213
|
x, y = bb["x"] + bb["width"] * int(px) / 100, bb["y"] + bb["height"] * int(py) / 100
|
|
@@ -202,7 +226,7 @@ class Client(PmAgentClient):
|
|
|
202
226
|
if await xcap.count():
|
|
203
227
|
await self.send_cap_help(xcap)
|
|
204
228
|
try:
|
|
205
|
-
await self.page.wait_for_url(lambda url: url != captcha_url)
|
|
229
|
+
await self.page.wait_for_url(lambda url: url != captcha_url, wait_until="commit")
|
|
206
230
|
except TimeoutError: # if page no changed -> captcha is undone
|
|
207
231
|
await self.page.screenshot()
|
|
208
232
|
raise CaptchaException(self.page.url)
|
|
@@ -233,41 +257,36 @@ class Client(PmAgentClient):
|
|
|
233
257
|
]
|
|
234
258
|
[await Fiat.update_or_create({"amount": amount}, cred=cred) for cred, amount in creds]
|
|
235
259
|
|
|
236
|
-
async def stop(self):
|
|
237
|
-
# save state
|
|
238
|
-
self.agent.state = await self.page.context.storage_state()
|
|
239
|
-
await self.agent.save()
|
|
240
|
-
# closing
|
|
241
|
-
await self.bot.stop()
|
|
242
|
-
await self.gmail.stop()
|
|
243
|
-
await self.page.context.close()
|
|
244
|
-
await self.page.context.browser.close()
|
|
245
|
-
|
|
246
260
|
|
|
247
261
|
async def _test():
|
|
248
262
|
from x_model import init_db
|
|
249
263
|
from xync_client.loader import TORM
|
|
250
264
|
|
|
251
265
|
_ = await init_db(TORM, True)
|
|
252
|
-
logging.basicConfig(level=logging.
|
|
253
|
-
|
|
266
|
+
logging.basicConfig(level=logging.INFO)
|
|
267
|
+
abot = Bot(PAY_TOKEN)
|
|
254
268
|
playwright: Playwright = await async_playwright().start()
|
|
255
|
-
agent = await PmAgent.get_or_none(pm__norm="volet", user__username_id=uid).prefetch_related(
|
|
256
|
-
"user__username__session"
|
|
257
|
-
)
|
|
258
|
-
if not agent:
|
|
259
|
-
raise Exception(f"No active user #{uid} with agent for volet!")
|
|
260
269
|
|
|
261
|
-
va = agent.client()
|
|
262
270
|
try:
|
|
263
|
-
await
|
|
264
|
-
await
|
|
265
|
-
await
|
|
271
|
+
o = await models.Order.create(ad_id=7, exid=1, amount=900, cred_id=522, taker_id=419)
|
|
272
|
+
await o.fetch_related("cred__pmcur__cur", "ad")
|
|
273
|
+
pma = await models.PmAgent.get(
|
|
274
|
+
active=True,
|
|
275
|
+
auth__isnull=False,
|
|
276
|
+
pm_id=o.cred.pmcur.pm_id,
|
|
277
|
+
user__person__actors=o.ad.maker_id,
|
|
278
|
+
user__status=UserStatus.ACTIVE,
|
|
279
|
+
).prefetch_related("pm", "user__gmail", "user__username__session")
|
|
280
|
+
t = models.Transfer(amount=9, created_at=now(), order=o)
|
|
281
|
+
pcl: Client = pma.client(abot)
|
|
282
|
+
pcl = await pcl.start(playwright, True, True)
|
|
283
|
+
await pcl.send(t)
|
|
284
|
+
await pcl.wait_for_payments()
|
|
266
285
|
except TimeoutError as te:
|
|
267
|
-
await
|
|
286
|
+
await pcl.receive(repr(te), photo=await pcl.page.screenshot())
|
|
268
287
|
raise te
|
|
269
288
|
finally:
|
|
270
|
-
await
|
|
289
|
+
await pcl.stop()
|
|
271
290
|
|
|
272
291
|
|
|
273
292
|
if __name__ == "__main__":
|
xync_client/Pms/Volet/api.py
CHANGED
|
@@ -88,16 +88,17 @@ class APIClient:
|
|
|
88
88
|
async def check_by_amount(self, amount: decimal, cur: str = "RUB", timeout: int = 5 * 60, past: int = 0):
|
|
89
89
|
hist: list = self.make_request("history", {"transactionDirection": "INCOMING", "count": 3, "from": 0})
|
|
90
90
|
if int(hist[0].amount) == int(amount):
|
|
91
|
-
return hist[0]
|
|
91
|
+
return hist[0]["amount"], hist[0]["id"]
|
|
92
92
|
await sleep(period)
|
|
93
93
|
past += period
|
|
94
94
|
if past < timeout:
|
|
95
95
|
return await self.check_by_amount(amount, cur, timeout, past)
|
|
96
|
-
return
|
|
96
|
+
return None, None
|
|
97
97
|
|
|
98
98
|
def check_by_id(self, tid: str):
|
|
99
|
-
t
|
|
100
|
-
|
|
99
|
+
if t := self.make_request("findTransaction", tid):
|
|
100
|
+
return t["amount"], t["id"]
|
|
101
|
+
return None, None
|
|
101
102
|
|
|
102
103
|
|
|
103
104
|
async def main():
|
xync_client/loader.py
CHANGED
|
@@ -5,6 +5,7 @@ from xync_schema import models
|
|
|
5
5
|
load_dotenv()
|
|
6
6
|
|
|
7
7
|
NET_TOKEN = env("NET_TOKEN")
|
|
8
|
+
PAY_TOKEN = env("PAY_TOKEN")
|
|
8
9
|
PG_DSN = f"postgres://{env('POSTGRES_USER')}:{env('POSTGRES_PASSWORD')}@{env('POSTGRES_HOST', 'xyncdbs')}:{env('POSTGRES_PORT', 5432)}/{env('POSTGRES_DB', env('POSTGRES_USER'))}"
|
|
9
10
|
TORM = {
|
|
10
11
|
"connections": {"default": PG_DSN},
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
xync_client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
xync_client/details.py,sha256=21itVPCgAtaYRR1H9J9oYudj95gafcFjExUN6QL17OI,1330
|
|
3
|
-
xync_client/loader.py,sha256=
|
|
3
|
+
xync_client/loader.py,sha256=hxf8ob50DO7r_qjr2qBoO7IyjkXeHHzVQ63YjXerjoU,600
|
|
4
4
|
xync_client/pm_unifier.py,sha256=T2Xh-tvcu114P2YBI6RK_XDiaIhyq6ABMrXDuXPlx7A,6541
|
|
5
|
-
xync_client/Abc/Agent.py,sha256=
|
|
5
|
+
xync_client/Abc/Agent.py,sha256=S2f8VEejC3LHT7oUJhilZCYuhcQVREX-3uzfc06ZyO4,5633
|
|
6
6
|
xync_client/Abc/Asset.py,sha256=hlgyFaU9byr2N2r8Heh-_ICx49SKuKxfRTUA4yQWmEw,454
|
|
7
7
|
xync_client/Abc/Auth.py,sha256=OPQXN7_XYQZP9431ylFksd6JDusbKG8N_1g6CXTZ6yY,1495
|
|
8
8
|
xync_client/Abc/BaseTest.py,sha256=vaAs5Z4HYV7k_C3zQz6JKO75s2hXtVbBI3-0Srkzv5Q,2388
|
|
9
9
|
xync_client/Abc/Ex.py,sha256=n41-XCjoIV-KpC_lK3jO049tQKbFmE0eDU3SDlgZTws,12986
|
|
10
10
|
xync_client/Abc/Exception.py,sha256=Sts7RpP370NBdjaH_cyXDdHtjge8zXNUGWCrKw49Zyk,482
|
|
11
|
-
xync_client/Abc/
|
|
11
|
+
xync_client/Abc/HasAbotUid.py,sha256=LsTHHjMHBauCwJoqgDa9Lx4R6xsDOHfsN4jM539Bpqg,279
|
|
12
|
+
xync_client/Abc/InAgent.py,sha256=XLf5czbxxEimsIIe653buoP7OsWZD6mc2w37q4TkNd0,703
|
|
12
13
|
xync_client/Abc/Order.py,sha256=7-FGIJu5z9aYi0A_eJV4F-cp_6Mz_izNpefexDQZvHw,2428
|
|
13
|
-
xync_client/Abc/PmAgent.py,sha256=
|
|
14
|
+
xync_client/Abc/PmAgent.py,sha256=5ihM_TY40SzHT4lZ_BDYtO1lqQoVAO3aXy_W8hRICHs,4359
|
|
14
15
|
xync_client/Abc/xtype.py,sha256=o1JEzWmEXCPddtlqWZ6HRTZTKX6SAnvsztbASj21zOQ,2584
|
|
15
16
|
xync_client/Binance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
17
|
xync_client/Binance/binance_async.py,sha256=LP2DZaHwkfsp_4Tjvetb-1ntjQtJfODF0OgZpoPx4KU,2688
|
|
@@ -34,8 +35,8 @@ xync_client/BitGet/agent.py,sha256=YVs3bDY0OcEJGU7m2A8chzO6PFhWDnQQrA-E6MVkBBg,3
|
|
|
34
35
|
xync_client/BitGet/ex.py,sha256=uEvvXuLaJv8o8BFi0bMA3XyBuTfVDWagAjLOHZl-xlE,3765
|
|
35
36
|
xync_client/BitGet/etype/ad.py,sha256=fysSW47wGYjSOPUqY864z857AJz4gjN-nOkI1Jxd27U,1838
|
|
36
37
|
xync_client/BitPapa/ex.py,sha256=U-RRB_RSOtErfRgxOZYWegZ_td_uZO37YKo3Jxchf_w,912
|
|
37
|
-
xync_client/Bybit/InAgent.py,sha256=
|
|
38
|
-
xync_client/Bybit/agent.py,sha256=
|
|
38
|
+
xync_client/Bybit/InAgent.py,sha256=50c9LGJuxe2T3qOL_2qNWIesJHmlmaj-oSx8dDnk9sY,26002
|
|
39
|
+
xync_client/Bybit/agent.py,sha256=wGYzvHGuKomNfEvFmosXQVscx9VXlzexc1Ic8sm-wA4,61894
|
|
39
40
|
xync_client/Bybit/ex.py,sha256=3oARvReBoDs90FzQY31-L-q_YU-TIRbvWB7z4lwESsA,4715
|
|
40
41
|
xync_client/Bybit/order.py,sha256=H4UIb8hxFGnw1hZuSbr0yZ4qeaCOIZOMc6jEst0ycBs,1713
|
|
41
42
|
xync_client/Bybit/web_earn.py,sha256=qjqS10xlFc8r40IhDdPZ0LxA2dFEGbvBGXdsrUUJCMo,3019
|
|
@@ -47,7 +48,7 @@ xync_client/Bybit/etype/order.py,sha256=rPXw9eHJW3afVPQDRiooUkHfQhmqJ5fTdZ4vKdO6
|
|
|
47
48
|
xync_client/Gate/ex.py,sha256=QbhB3u7TWnvVGD-AknB2nay6KZjEXQ-1JT9UacX4sWI,3735
|
|
48
49
|
xync_client/Gate/premarket.py,sha256=IW-CgkmNJePJR2j_NRfULNKTePMX35XlhldqdiO76zY,2138
|
|
49
50
|
xync_client/Gate/etype/ad.py,sha256=-EwtFcOWWvtE6UjaOdsuXWDTCVjAIRK0kSEsqPP4Yls,1296
|
|
50
|
-
xync_client/Gmail/__init__.py,sha256=
|
|
51
|
+
xync_client/Gmail/__init__.py,sha256=wNEUA1L9lvLtYoIv2fHEsb6msO9oB9QTF2OkzuCPMcM,4778
|
|
51
52
|
xync_client/Htx/agent.py,sha256=rOURgTeY9TsA-IzT78l5-Ze91i0x1PY683mrX38MSgs,7356
|
|
52
53
|
xync_client/Htx/earn.py,sha256=jL6eRwytZEMRom_3bFm1DYthi_GFg-E1Mm3ZDXENHSg,2386
|
|
53
54
|
xync_client/Htx/ex.py,sha256=GuWK5lA_MvtSd-0DhKf2MAstKvZMHhc3BIiZSgdwZv4,6074
|
|
@@ -72,14 +73,14 @@ xync_client/Pms/Alfa/state.json,sha256=MKE6vl-JsJO9PNCVqoQgBgYZTgYkHCas7USwl8QFt
|
|
|
72
73
|
xync_client/Pms/MTS/__init__.py,sha256=P_E7W46IZEk8RsEgl7H1xV3JplMT5l9vYQYTYyNbyQ8,2101
|
|
73
74
|
xync_client/Pms/Ozon/__init__.py,sha256=EvQZDSPv0fOT2hNCTP44nXHOIEQvP5bQf_7HVLiZc2I,4123
|
|
74
75
|
xync_client/Pms/Payeer/.gitignore,sha256=sWORdRp8ROppV2CsMEDJ3M_SokrNWCf8b1hlaNs64hg,12
|
|
75
|
-
xync_client/Pms/Payeer/__init__.py,sha256=
|
|
76
|
+
xync_client/Pms/Payeer/__init__.py,sha256=fxYbRHq1x1pCKQuo-blydZBWrYMSebuYZjRrD6k-e-E,9905
|
|
76
77
|
xync_client/Pms/Payeer/api.py,sha256=bb8qrlPYyWafel1VR-2nate6xBeRZAVciFJblHygfAs,549
|
|
77
|
-
xync_client/Pms/Payeer/login.py,sha256=
|
|
78
|
+
xync_client/Pms/Payeer/login.py,sha256=GyNwB-GKE_1nlkbZJ0KNI-EnCT0j_S9ogFdn-ufb-zU,3053
|
|
78
79
|
xync_client/Pms/Sber/__init__.py,sha256=dxQfd9ZPhFTc_C4xrwaxrV6p0SijDCLNzBeUv3oQG38,4926
|
|
79
80
|
xync_client/Pms/Sber/utils.py,sha256=gIeJspwvoBbOBt-fjxwW4WDHPoL2Evs8LVufsjrFOfo,1870
|
|
80
81
|
xync_client/Pms/Tinkoff/__init__.py,sha256=ZyLvBEUn-vh-85oPUUDS586AHgvx3c-mkQE3yBQtbw8,5580
|
|
81
|
-
xync_client/Pms/Volet/__init__.py,sha256=
|
|
82
|
-
xync_client/Pms/Volet/api.py,sha256=
|
|
82
|
+
xync_client/Pms/Volet/__init__.py,sha256=Zn15NJRZH9pwwVjiR1Ao77MN6mTaA45B42fr0NJweno,12047
|
|
83
|
+
xync_client/Pms/Volet/api.py,sha256=6_dH2rzmyyvha3PeoiZdSltiAzKDWn8roSUJOAErX4M,3673
|
|
83
84
|
xync_client/Pms/Volet/pl.py,sha256=l7lvUrpjFoObXPHaseOIAcSbkNqJdpy6OLDutxYJH3U,2451
|
|
84
85
|
xync_client/Pms/Volet/_todo_req/req.mjs,sha256=ut3Jw37rL5lY7SskjZ9f1l0VE33tuP-PZEYUTcJMc2I,817
|
|
85
86
|
xync_client/Pms/Volet/_todo_req/req.py,sha256=mKvdPrb-lkQ98Ws92_oBKu5yqyU8Krxy9XwuIhdsBao,1570
|
|
@@ -95,7 +96,7 @@ xync_client/TgWallet/order.py,sha256=BOmBx5WWfJv0-_-A8DcR-Xd8utqO_VTmSqSegm0cteQ
|
|
|
95
96
|
xync_client/TgWallet/pyd.py,sha256=Ys3E8b3RLuyQ26frWT0F0BorkNxVpxnd18tY4Gp9dik,5636
|
|
96
97
|
xync_client/TgWallet/pyro.py,sha256=2K7QWdo48k4MbbgQt90gdz_HiPck69Njm4xaMjIVgoo,1440
|
|
97
98
|
xync_client/TgWallet/web.py,sha256=kDcv9SKKQPe91mw1qJBpbuyKYCAmZdfdHJylHumLBVU,1608
|
|
98
|
-
xync_client-0.0.
|
|
99
|
-
xync_client-0.0.
|
|
100
|
-
xync_client-0.0.
|
|
101
|
-
xync_client-0.0.
|
|
99
|
+
xync_client-0.0.145.dist-info/METADATA,sha256=Czpg727G7V7cQcbZ7mDkx0Egv3bf3xtoSgH5K1Y8QgU,1037
|
|
100
|
+
xync_client-0.0.145.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
101
|
+
xync_client-0.0.145.dist-info/top_level.txt,sha256=bmYEVIIrD3v7yFwH-X15pEfRvzhuAdfsAZ2igvNI4O8,12
|
|
102
|
+
xync_client-0.0.145.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|