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.

Files changed (125) hide show
  1. {xync_client-0.0.144/xync_client.egg-info → xync_client-0.0.145}/PKG-INFO +1 -1
  2. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Agent.py +19 -5
  3. xync_client-0.0.145/xync_client/Abc/HasAbotUid.py +10 -0
  4. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/InAgent.py +5 -3
  5. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/PmAgent.py +33 -22
  6. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/InAgent.py +46 -29
  7. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/agent.py +11 -8
  8. xync_client-0.0.145/xync_client/Gmail/__init__.py +124 -0
  9. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Payeer/__init__.py +32 -25
  10. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Payeer/login.py +6 -2
  11. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Volet/__init__.py +80 -61
  12. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Volet/api.py +5 -4
  13. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/loader.py +1 -0
  14. {xync_client-0.0.144 → xync_client-0.0.145/xync_client.egg-info}/PKG-INFO +1 -1
  15. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client.egg-info/SOURCES.txt +1 -0
  16. xync_client-0.0.144/xync_client/Gmail/__init__.py +0 -116
  17. {xync_client-0.0.144 → xync_client-0.0.145}/.env.sample +0 -0
  18. {xync_client-0.0.144 → xync_client-0.0.145}/.gitignore +0 -0
  19. {xync_client-0.0.144 → xync_client-0.0.145}/.pre-commit-config.yaml +0 -0
  20. {xync_client-0.0.144 → xync_client-0.0.145}/README.md +0 -0
  21. {xync_client-0.0.144 → xync_client-0.0.145}/__init__.py +0 -0
  22. {xync_client-0.0.144 → xync_client-0.0.145}/makefile +0 -0
  23. {xync_client-0.0.144 → xync_client-0.0.145}/pyproject.toml +0 -0
  24. {xync_client-0.0.144 → xync_client-0.0.145}/setup.cfg +0 -0
  25. {xync_client-0.0.144 → xync_client-0.0.145}/tests/TestAgent.py +0 -0
  26. {xync_client-0.0.144 → xync_client-0.0.145}/tests/TestAsset.py +0 -0
  27. {xync_client-0.0.144 → xync_client-0.0.145}/tests/TestEx.py +0 -0
  28. {xync_client-0.0.144 → xync_client-0.0.145}/tests/TestOrder.py +0 -0
  29. {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Binance/test_binance.py +0 -0
  30. {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
  31. {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
  32. {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Gate/test_gate.py +0 -0
  33. {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Wallet/test_agent.py +0 -0
  34. {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/Wallet/test_ex.py +0 -0
  35. {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/__init__.py +0 -0
  36. {xync_client-0.0.144 → xync_client-0.0.145}/tests/_todo_refact/_test_ex.py +0 -0
  37. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Asset.py +0 -0
  38. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Auth.py +0 -0
  39. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/BaseTest.py +0 -0
  40. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Ex.py +0 -0
  41. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Exception.py +0 -0
  42. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/Order.py +0 -0
  43. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Abc/xtype.py +0 -0
  44. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/__init__.py +0 -0
  45. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/binance_async.py +0 -0
  46. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/earn_api.py +0 -0
  47. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/etype/ad.py +0 -0
  48. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/etype/pm.py +0 -0
  49. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/ex.py +0 -0
  50. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/exceptions.py +0 -0
  51. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/sapi.py +0 -0
  52. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Binance/web_c2c.py +0 -0
  53. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/__init__.py +0 -0
  54. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/agent.py +0 -0
  55. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/base.py +0 -0
  56. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/etype/ad.py +0 -0
  57. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/etype/pm.py +0 -0
  58. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/ex.py +0 -0
  59. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/req.mjs +0 -0
  60. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BingX/sign.js +0 -0
  61. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BitGet/__init__.py +0 -0
  62. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BitGet/agent.py +0 -0
  63. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BitGet/etype/ad.py +0 -0
  64. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BitGet/ex.py +0 -0
  65. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/BitPapa/ex.py +0 -0
  66. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/etype/ad.py +0 -0
  67. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/etype/cred.py +0 -0
  68. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/etype/order.py +0 -0
  69. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/ex.py +0 -0
  70. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/order.py +0 -0
  71. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/web_earn.py +0 -0
  72. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/web_p2p.py +0 -0
  73. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Bybit/ws.py +0 -0
  74. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Gate/etype/ad.py +0 -0
  75. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Gate/ex.py +0 -0
  76. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Gate/premarket.py +0 -0
  77. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/agent.py +0 -0
  78. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/earn.py +0 -0
  79. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/etype/__init__.py +0 -0
  80. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/etype/ad.py +0 -0
  81. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/etype/cred.py +0 -0
  82. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/etype/pm.py +0 -0
  83. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/etype/test.py +0 -0
  84. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Htx/ex.py +0 -0
  85. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/KuCoin/etype/ad.py +0 -0
  86. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/KuCoin/etype/pm.py +0 -0
  87. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/KuCoin/ex.py +0 -0
  88. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/KuCoin/web.py +0 -0
  89. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Mexc/etype/ad.py +0 -0
  90. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Mexc/etype/pm.py +0 -0
  91. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Mexc/ex.py +0 -0
  92. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Okx/etype/ad.py +0 -0
  93. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Okx/etype/pm.py +0 -0
  94. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Okx/ex.py +0 -0
  95. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/.gitignore +0 -0
  96. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Alfa/__init__.py +0 -0
  97. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Alfa/state.json +0 -0
  98. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/MTS/__init__.py +0 -0
  99. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Ozon/__init__.py +0 -0
  100. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Payeer/.gitignore +0 -0
  101. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Payeer/api.py +0 -0
  102. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Sber/__init__.py +0 -0
  103. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Sber/utils.py +0 -0
  104. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Tinkoff/__init__.py +0 -0
  105. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Volet/_todo_req/req.mjs +0 -0
  106. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Volet/_todo_req/req.py +0 -0
  107. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Volet/pl.py +0 -0
  108. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Xync/__main__.py +0 -0
  109. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Xync/ed.py +0 -0
  110. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/Pms/Yandex/__init__.py +0 -0
  111. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/agent.py +0 -0
  112. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/asset.py +0 -0
  113. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/auth.py +0 -0
  114. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/ex.py +0 -0
  115. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/inAgent.py +0 -0
  116. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/order.py +0 -0
  117. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/pyd.py +0 -0
  118. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/pyro.py +0 -0
  119. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/TgWallet/web.py +0 -0
  120. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/__init__.py +0 -0
  121. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/details.py +0 -0
  122. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client/pm_unifier.py +0 -0
  123. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client.egg-info/dependency_links.txt +0 -0
  124. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client.egg-info/requires.txt +0 -0
  125. {xync_client-0.0.144 → xync_client-0.0.145}/xync_client.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xync-client
3
- Version: 0.0.144
3
+ Version: 0.0.145
4
4
  Author-email: Mike Artemiev <mixartemev@gmail.com>
5
5
  Project-URL: Homepage, https://gitlab.com/XyncNet/client
6
6
  Project-URL: Repository, https://gitlab.com/XyncNet/client
@@ -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
- def __init__(self, actor: Actor, bot: FileClient, headers: dict[str, str] = None, cookies: dict[str, str] = None):
16
- self.actor: Actor = actor
17
- super().__init__(actor.ex.host_p2p, headers, cookies)
18
- self.ex_client: BaseExClient = self.actor.ex.client(bot)
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 Actor
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, actor: Actor, bot: FileClient):
14
- self.agent_client: BaseAgentClient = actor.client(bot)
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 playwright.async_api import Page, Playwright
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
- bot: FileClient | UserClient
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, userbot: bool = False) -> "PmAgentClient":
36
- bot = FileClient(NET_TOKEN)
37
- self.bot = UserClient(self.uid, bot) if userbot else bot
38
- await self.bot.start()
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="chromium" if headed else "chromium-headless-shell", headless=not headed
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.bot.send(self.norm + " not logged in!", self.uid, photo=await self.page.screenshot())
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 = datetime.now()
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 < datetime.now() - timedelta(minutes=1):
63
- await self.page.reload()
64
- self.last_active = datetime.now()
65
- await self.bot.send(self.norm + " stoped", self.uid)
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.bot.stop()
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, dest, amount: int, cur: str) -> tuple[int, bytes, float]: ...
90
+ async def send(self, t: Transfer) -> tuple[int, bytes] | float: ...
83
91
 
84
92
  @abstractmethod # проверка поступления определенной суммы за последние пол часа (минимум), return точную сумму
85
- async def check_in(self, amount: int | Decimal | float, cur: str, tid: str | int = None) -> float | None: ...
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.actor.agent.auth["deviceId"], t["result"], ts)
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
- dest = order.paymentTermList[0].accountNo
100
- if not re.match(r"^([PpРр])\d{7,10}\b", dest):
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, dest)
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.paymentTermList[0].accountNo
144
- == order.paymentTermList[0].accountNo
145
- and int(order.createDate) < int(o["createDate"]) + 3600 * 000
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
- await self.send_payment(order_db, g.group())
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, dest):
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 {dest} #{t.pmid}!",
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(f"Order# {order_db.exid}: Double send {fmt_am}{cur} to {dest} #{t.pmid}!")
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, _ = await models.Transfer.update_or_create({"order": order_db, "amount": order_db.amount}, pmid=tid)
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
- _ = await init_db(TORM, True)
408
+ cn = await init_db(TORM, True)
400
409
  logging.basicConfig(level=logging.INFO)
401
410
 
402
- actor = (
403
- await models.Actor.filter(
404
- ex_id=4,
405
- agent__auth__isnull=False,
406
- person__user__status=UserStatus.ACTIVE,
407
- person__user__pm_agents__isnull=False,
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("ex", "agent", "person__user__pm_agents__user", "person__user__pm_agents__pm")
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 = actor.in_client(b)
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 actor.person.user.pm_agents:
419
- pcl = pma.client()
420
- cl.pmacs[pma.pm_id] = await pcl.start(await async_playwright().start(), False)
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(e.__repr__(), actor.person.user.username_id)
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, actor: Actor, bot: FileClient, **kwargs):
91
- super().__init__(actor, bot, **kwargs)
92
- self.api = P2P(testnet=False, api_key=actor.agent.auth["key"], api_secret=actor.agent.auth["sec"])
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
- _ = await init_db(TORM)
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.Actor.filter(ex_id=4, agent__isnull=False).prefetch_related("ex", "agent", "person__user").first()
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())