xync-client 0.0.144__tar.gz → 0.0.145__tar.gz
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-0.0.144/xync_client.egg-info → xync_client-0.0.145}/PKG-INFO +1 -1
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Agent.py +19 -5
- xync_client-0.0.145/xync_client/Abc/HasAbotUid.py +10 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/InAgent.py +5 -3
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/PmAgent.py +33 -22
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/InAgent.py +46 -29
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/agent.py +11 -8
- xync_client-0.0.145/xync_client/Gmail/__init__.py +124 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Payeer/__init__.py +32 -25
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Payeer/login.py +6 -2
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Volet/__init__.py +80 -61
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Volet/api.py +5 -4
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/loader.py +1 -0
- {xync_client-0.0.144 → xync_client-0.0.145/xync_client.egg-info}/PKG-INFO +1 -1
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client.egg-info/SOURCES.txt +1 -0
- xync_client-0.0.144/xync_client/Gmail/__init__.py +0 -116
- {xync_client-0.0.144 → xync_client-0.0.145}/.env.sample +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/.gitignore +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/.pre-commit-config.yaml +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/README.md +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/makefile +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/pyproject.toml +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/setup.cfg +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/TestAgent.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/TestAsset.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/TestEx.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/TestOrder.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Binance/test_binance.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Gate/test_gate.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Wallet/test_agent.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Wallet/test_ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/_test_ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Asset.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Auth.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/BaseTest.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Exception.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Order.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/xtype.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/binance_async.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/earn_api.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/etype/ad.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/etype/pm.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/exceptions.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/sapi.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/web_c2c.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/agent.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/base.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/etype/ad.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/etype/pm.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/req.mjs +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/sign.js +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BitGet/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BitGet/agent.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BitGet/etype/ad.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BitGet/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BitPapa/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/etype/ad.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/etype/cred.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/etype/order.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/order.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/web_earn.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/web_p2p.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/ws.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Gate/etype/ad.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Gate/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Gate/premarket.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/agent.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/earn.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/etype/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/etype/ad.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/etype/cred.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/etype/pm.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/etype/test.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/KuCoin/etype/ad.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/KuCoin/etype/pm.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/KuCoin/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/KuCoin/web.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Mexc/etype/ad.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Mexc/etype/pm.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Mexc/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Okx/etype/ad.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Okx/etype/pm.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Okx/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/.gitignore +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Alfa/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Alfa/state.json +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/MTS/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Ozon/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Payeer/.gitignore +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Payeer/api.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Sber/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Sber/utils.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Tinkoff/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Volet/_todo_req/req.mjs +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Volet/_todo_req/req.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Volet/pl.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Xync/__main__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Xync/ed.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Yandex/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/agent.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/asset.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/auth.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/ex.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/inAgent.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/order.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/pyd.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/pyro.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/web.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/__init__.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/details.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/pm_unifier.py +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client.egg-info/dependency_links.txt +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client.egg-info/requires.txt +0 -0
- {xync_client-0.0.144 → xync_client-0.0.145}/xync_client.egg-info/top_level.txt +0 -0
|
@@ -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)
|
|
@@ -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: ...
|
|
@@ -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
|
|
@@ -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
|
|
|
@@ -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()
|
|
@@ -0,0 +1,124 @@
|
|
|
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
|
|
11
|
+
from xync_schema.models import User, Gmail
|
|
12
|
+
|
|
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)
|
|
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)
|
|
37
|
+
|
|
38
|
+
# Сохраняем credentials для следующего запуска
|
|
39
|
+
user.gmail.token = pickle.dumps(creds)
|
|
40
|
+
|
|
41
|
+
self.service = build("gmail", "v1", credentials=creds)
|
|
42
|
+
self.uid = user.username_id
|
|
43
|
+
|
|
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, # Только последнее письмо
|
|
72
|
+
)
|
|
73
|
+
.execute()
|
|
74
|
+
)
|
|
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
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
async def _test():
|
|
112
|
+
from x_model import init_db
|
|
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())
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
from asyncio import run
|
|
123
|
+
|
|
124
|
+
run(_test())
|