xync-client 0.0.43.dev41__tar.gz → 0.0.43.dev43__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.
Files changed (115) hide show
  1. {xync_client-0.0.43.dev41/xync_client.egg-info → xync_client-0.0.43.dev43}/PKG-INFO +1 -1
  2. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Abc/Ex.py +12 -12
  3. xync_client-0.0.43.dev43/xync_client/Pyrogram/base.py +42 -0
  4. xync_client-0.0.43.dev43/xync_client/Pyrogram/bot.py +11 -0
  5. xync_client-0.0.43.dev43/xync_client/Pyrogram/file.py +61 -0
  6. xync_client-0.0.43.dev43/xync_client/Pyrogram/pyro.py +74 -0
  7. xync_client-0.0.43.dev43/xync_client/Pyrogram/user.py +104 -0
  8. xync_client-0.0.43.dev43/xync_client/TgWallet/pyro.py +43 -0
  9. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/loader.py +1 -3
  10. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43/xync_client.egg-info}/PKG-INFO +1 -1
  11. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client.egg-info/SOURCES.txt +5 -0
  12. xync_client-0.0.43.dev41/xync_client/TgWallet/pyro.py +0 -147
  13. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/.env.sample +0 -0
  14. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/.gitignore +0 -0
  15. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/.pre-commit-config.yaml +0 -0
  16. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/README.md +0 -0
  17. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/makefile +0 -0
  18. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/pyproject.toml +0 -0
  19. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/setup.cfg +0 -0
  20. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/TestAgent.py +0 -0
  21. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/TestAsset.py +0 -0
  22. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/TestEx.py +0 -0
  23. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/TestOrder.py +0 -0
  24. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/_todo_refact/Binance/test_binance.py +0 -0
  25. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
  26. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
  27. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/_todo_refact/Gate/test_gate.py +0 -0
  28. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/_todo_refact/Htx/test_htx_p2p.py +0 -0
  29. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/_todo_refact/Wallet/test_agent.py +0 -0
  30. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/_todo_refact/Wallet/test_ex.py +0 -0
  31. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/_todo_refact/__init__.py +0 -0
  32. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/tests/_todo_refact/_test_ex.py +0 -0
  33. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Abc/Agent.py +0 -0
  34. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Abc/Asset.py +0 -0
  35. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Abc/AuthTrait.py +0 -0
  36. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Abc/Base.py +0 -0
  37. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Abc/BaseTest.py +0 -0
  38. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Abc/InAgent.py +0 -0
  39. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Abc/Order.py +0 -0
  40. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Abc/types.py +0 -0
  41. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Binance/__init__.py +0 -0
  42. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Binance/binance_async.py +0 -0
  43. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Binance/earn_api.py +0 -0
  44. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Binance/etype/ad.py +0 -0
  45. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Binance/etype/pm.py +0 -0
  46. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Binance/ex.py +0 -0
  47. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Binance/exceptions.py +0 -0
  48. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Binance/sapi.py +0 -0
  49. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Binance/web_c2c.py +0 -0
  50. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BingX/__init__.py +0 -0
  51. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BingX/agent.py +0 -0
  52. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BingX/base.py +0 -0
  53. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BingX/etype/ad.py +0 -0
  54. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BingX/etype/pm.py +0 -0
  55. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BingX/ex.py +0 -0
  56. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BingX/req.mjs +0 -0
  57. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BingX/sign.js +0 -0
  58. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BitGet/__init__.py +0 -0
  59. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BitGet/agent.py +0 -0
  60. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BitGet/etype/ad.py +0 -0
  61. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BitGet/ex.py +0 -0
  62. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BitGet/req.mjs +0 -0
  63. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/BitPapa/ex.py +0 -0
  64. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Bybit/InAgent.py +0 -0
  65. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Bybit/agent.py +0 -0
  66. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Bybit/etype/ad.py +0 -0
  67. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Bybit/etype/cred.py +0 -0
  68. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Bybit/etype/order.py +0 -0
  69. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Bybit/ex.py +0 -0
  70. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Bybit/web_earn.py +0 -0
  71. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Bybit/web_p2p.py +0 -0
  72. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Bybit/ws.py +0 -0
  73. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Gate/etype/ad.py +0 -0
  74. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Gate/ex.py +0 -0
  75. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Gate/premarket.py +0 -0
  76. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Htx/agent.py +0 -0
  77. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Htx/earn.py +0 -0
  78. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Htx/etype/__init__.py +0 -0
  79. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Htx/etype/ad.py +0 -0
  80. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Htx/etype/cred.py +0 -0
  81. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Htx/etype/pm.py +0 -0
  82. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Htx/etype/test.py +0 -0
  83. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Htx/ex.py +0 -0
  84. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/KuCoin/etype/ad.py +0 -0
  85. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/KuCoin/etype/pm.py +0 -0
  86. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/KuCoin/ex.py +0 -0
  87. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/KuCoin/web.py +0 -0
  88. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Mexc/etype/ad.py +0 -0
  89. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Mexc/etype/pm.py +0 -0
  90. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Mexc/ex.py +0 -0
  91. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Okx/etype/ad.py +0 -0
  92. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Okx/etype/pm.py +0 -0
  93. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Okx/ex.py +0 -0
  94. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Pms/Alfa/__init__.py +0 -0
  95. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Pms/Alfa/state.json +0 -0
  96. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Pms/Sber/__init__.py +0 -0
  97. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Pms/Tinkoff/__init__.py +0 -0
  98. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Pms/Tinkoff/state.json +0 -0
  99. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Pms/Volet/__init__.py +0 -0
  100. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Pms/Volet/_todo_req/req.mjs +0 -0
  101. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Pms/Volet/_todo_req/req.py +0 -0
  102. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/Pms/Volet/api.py +0 -0
  103. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/TgWallet/agent.py +0 -0
  104. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/TgWallet/asset.py +0 -0
  105. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/TgWallet/auth.py +0 -0
  106. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/TgWallet/ex.py +0 -0
  107. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/TgWallet/inAgent.py +0 -0
  108. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/TgWallet/order.py +0 -0
  109. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/TgWallet/pyd.py +0 -0
  110. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/TgWallet/web.py +0 -0
  111. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/__init__.py +0 -0
  112. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client/pm_unifier.py +0 -0
  113. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client.egg-info/dependency_links.txt +0 -0
  114. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/xync_client.egg-info/requires.txt +0 -0
  115. {xync_client-0.0.43.dev41 → xync_client-0.0.43.dev43}/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.43.dev41
3
+ Version: 0.0.43.dev43
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
@@ -6,14 +6,14 @@ import msgspec
6
6
  from aiohttp import ClientSession
7
7
  from msgspec import Struct
8
8
  from tortoise.exceptions import MultipleObjectsReturned, IntegrityError
9
+ from xync_client.Pyrogram.file import FileClient
9
10
  from xync_schema import models
10
- from xync_schema.enums import FileType
11
+ from xync_schema.enums import FileType, df_curs
11
12
  from xync_schema.types import CurEx, CoinEx, BaseAd, BaseAdIn
12
13
 
13
14
  from xync_client.Abc.Base import BaseClient, MapOfIdsList
14
15
  from xync_client.Abc.types import PmEx
15
- from xync_client.TgWallet.pyro import PyroClient
16
- from xync_client.loader import bot
16
+ from xync_client.loader import TOKEN
17
17
  from xync_client.pm_unifier import PmUnifier, PmUni
18
18
 
19
19
 
@@ -164,19 +164,19 @@ class BaseExClient(BaseClient):
164
164
  else:
165
165
  pmin = models.Pm.validate({**pmu.model_dump(), "country_id": country_id, "typ": pmex.typ})
166
166
  if c := df_curs.get(pmu.norm):
167
- pmin.df_cur_id = curs[c].id
167
+ pmin.df_cur_id = await models.Cur.get(ticker=c).values_list("id", flat=True)
168
168
  try:
169
169
  pms[k], _ = await models.Pm.update_or_create(**pmin.df_unq())
170
170
  except (MultipleObjectsReturned, IntegrityError) as e:
171
171
  raise e
172
172
  prev = k, pmu.norm, pmex.name, pmu.country
173
- await models.Pmcur.update_or_create(
174
- cur=await models.Cur.get(ticker="THB"), pm=await models.Pm.get(norm="cash in person")
175
- )
173
+ await models.Pmcur.update_or_create( # todo: NA HU YA???
174
+ cur=await models.Cur.get(ticker="THB"), pm=await models.Pm.get(norm="cash in person")
175
+ )
176
176
 
177
177
  # Pmexs
178
- pbot = PyroClient(bot)
179
- await pbot.app.start()
178
+ pbot = FileClient(TOKEN)
179
+ await pbot.start()
180
180
  async with ClientSession(headers=getattr(self, "logo_headers", None)) as ss:
181
181
  pmexs = [
182
182
  models.Pmex(
@@ -189,7 +189,7 @@ class BaseExClient(BaseClient):
189
189
  )
190
190
  for k, pm in pms.items()
191
191
  ]
192
- await pbot.app.stop()
192
+ await pbot.stop()
193
193
 
194
194
  await models.Pmex.bulk_create(pmexs, on_conflict=["ex_id", "exid"], update_fields=["pm_id", "logo_id", "name"])
195
195
  # Pmex banks
@@ -214,7 +214,7 @@ class BaseExClient(BaseClient):
214
214
  # pmcurexs = [Pmcurex(pmcur=pmcur, ex=self.ex) for pmcur in pmcurs]
215
215
  # await Pmcurex.bulk_create(pmcurexs)
216
216
 
217
- async def logo_save(self, url: str | None, pbot: PyroClient, ss: ClientSession) -> models.File | None:
217
+ async def logo_save(self, url: str | None, pbot: FileClient, ss: ClientSession) -> models.File | None:
218
218
  if url or (file := None):
219
219
  if not url.startswith("https:"):
220
220
  if not url.startswith("/"):
@@ -224,7 +224,7 @@ class BaseExClient(BaseClient):
224
224
  resp = await ss.get(url)
225
225
  if resp.ok:
226
226
  byts = await resp.read()
227
- upf, ref = await pbot.save_file(byts, resp.content_type)
227
+ upf, ref = await pbot.save_doc(byts, resp.content_type)
228
228
  await sleep(0.34)
229
229
  typ = FileType[resp.content_type.split("/")[-1]]
230
230
  file, _ = await models.File.update_or_create({"ref": ref, "size": len(byts), "typ": typ}, name=url)
@@ -0,0 +1,42 @@
1
+ from asyncio import sleep
2
+ from typing import Literal
3
+
4
+ from pyrogram import Client
5
+ from pyrogram.filters import chat
6
+ from pyrogram.handlers import MessageHandler
7
+ from pyrogram.types import Message
8
+
9
+ from xync_client.Pyrogram.storage import PgStorage
10
+ from xync_client.loader import TG_API_ID, TG_API_HASH
11
+
12
+ AuthTopic = Literal["phone", "code", "pass"]
13
+
14
+
15
+ class BaseClient(Client):
16
+ storage: PgStorage
17
+
18
+ def __init__(self, name: str, *args, **kwargs):
19
+ super().__init__(
20
+ name,
21
+ TG_API_ID,
22
+ TG_API_HASH,
23
+ storage_engine=PgStorage(name),
24
+ # device_model="iPhone 17 Air", app_version="XyncNet 1.0", system_version="iOS 19.0.1",
25
+ *args,
26
+ **kwargs,
27
+ )
28
+
29
+ async def wait_from(self, uid: int, topic: str, past: int = 0, timeout: int = 10) -> str:
30
+ handler = MessageHandler(self.got_msg, chat(uid))
31
+ self.add_handler(handler)
32
+ while past < timeout:
33
+ if txt := self.storage.session.state.get(uid, {}).pop(topic, None):
34
+ self.remove_handler(handler)
35
+ return txt
36
+ await sleep(1)
37
+ past += 1
38
+ return await self.wait_from(uid, topic, past, timeout)
39
+
40
+ async def got_msg(self, _, msg: Message):
41
+ if topic := self.storage.session.state.get(msg.from_user.id, {}).pop("waiting_for", None):
42
+ self.storage.session.state[msg.from_user.id][topic] = msg.text
@@ -0,0 +1,11 @@
1
+ from xync_client.Pyrogram.base import AuthTopic
2
+
3
+ from xync_client.Pyrogram.base import BaseClient
4
+
5
+
6
+ class BotClient(BaseClient):
7
+ def __init__(self, token: str):
8
+ super().__init__(token.split(":")[0], bot_token=token)
9
+
10
+ async def wait_auth_from(self, uid: int, topic: AuthTopic, past: int = 0, timeout: int = 60) -> str:
11
+ return await super().wait_from(uid, topic, past, timeout)
@@ -0,0 +1,61 @@
1
+ from io import BytesIO
2
+
3
+ from pyrogram.raw.functions.messages import UploadMedia
4
+ from pyrogram.raw.functions.upload import GetFile
5
+ from pyrogram.raw.types import (
6
+ MessageMediaDocument,
7
+ InputMediaUploadedDocument,
8
+ InputPeerSelf,
9
+ MessageMediaPhoto,
10
+ InputMediaUploadedPhoto,
11
+ InputDocumentFileLocation,
12
+ InputPhotoFileLocation,
13
+ )
14
+ from pyrogram.raw.types.upload import File
15
+ from pyrogram.types import Message
16
+
17
+ from xync_client.Pyrogram.bot import BotClient
18
+
19
+
20
+ class FileClient(BotClient):
21
+ @staticmethod
22
+ def ref_enc(ph_id: int, access_hash: int, ref: bytes) -> bytes:
23
+ return ph_id.to_bytes(8, "big") + access_hash.to_bytes(8, "big", signed=True) + ref
24
+
25
+ @staticmethod
26
+ def ref_dec(full_ref: bytes) -> tuple[int, int, bytes]:
27
+ pid, ah = int.from_bytes(full_ref[:8], "big"), int.from_bytes(full_ref[8:16], "big", signed=True)
28
+ return pid, ah, full_ref[16:]
29
+
30
+ async def send_img(self, txt: str, byts: bytes, uid="me"):
31
+ return await self.send_photo(uid, BytesIO(byts), txt)
32
+
33
+ async def save_doc(self, byts: bytes, ctype: str) -> tuple[MessageMediaDocument, bytes]:
34
+ in_file = await self.save_file(BytesIO(byts))
35
+ imud = InputMediaUploadedDocument(file=in_file, mime_type=ctype, attributes=[])
36
+ upf: MessageMediaDocument = await self.invoke(UploadMedia(peer=InputPeerSelf(), media=imud))
37
+ return upf, (
38
+ upf.document.id.to_bytes(8, "big")
39
+ + upf.document.access_hash.to_bytes(8, "big", signed=True)
40
+ + upf.document.file_reference
41
+ )
42
+
43
+ async def save_photo(self, file: bytes) -> tuple[MessageMediaPhoto, bytes]:
44
+ in_file = await self.save_file(BytesIO(file))
45
+ upm = UploadMedia(peer=InputPeerSelf(), media=InputMediaUploadedPhoto(file=in_file))
46
+ upp: MessageMediaPhoto = await self.invoke(upm)
47
+ return upp, self.ref_enc(upp.photo.id, upp.photo.access_hash, upp.photo.file_reference)
48
+
49
+ async def get_doc(self, fid: bytes) -> File:
50
+ pid, ah, ref = self.ref_dec(fid)
51
+ loc = InputDocumentFileLocation(id=pid, access_hash=ah, file_reference=ref, thumb_size="x")
52
+ return await self.invoke(GetFile(location=loc, offset=0, limit=512 * 1024))
53
+
54
+ async def get_photo(self, fid: bytes, st: str) -> File:
55
+ pid, ah, ref = self.ref_dec(fid)
56
+ loc = InputPhotoFileLocation(id=pid, access_hash=ah, file_reference=ref, thumb_size=st)
57
+ return await self.invoke(GetFile(location=loc, offset=0, limit=512 * 1024))
58
+
59
+ async def bot_got_msg(self, _, msg: Message):
60
+ if state := self.storage.session.state.pop("bot", None):
61
+ self.storage.session.state[state] = msg.text
@@ -0,0 +1,74 @@
1
+ from asyncio import run, sleep
2
+
3
+ from pyrogram.errors import UserNotParticipant
4
+ from pyrogram.raw.functions.photos import GetUserPhotos
5
+ from pyrogram.raw.types.photos import Photos
6
+ from pyrogram.types import Chat, ChatPrivileges
7
+ from x_model import init_db
8
+
9
+ from xync_client.Pyrogram.client import UserClient, BotClient
10
+ from xync_client.Pyrogram.pg_storage import PgStorage
11
+ from xync_client.loader import TG_API_ID, TG_API_HASH, PG_DSN, TOKEN
12
+ from xync_schema import models
13
+
14
+
15
+ class PyroClient:
16
+ max_privs = ChatPrivileges(
17
+ can_manage_chat=True, # default
18
+ can_delete_messages=True,
19
+ can_delete_stories=True, # Channels only
20
+ can_manage_video_chats=True, # Groups and supergroups only
21
+ can_restrict_members=True,
22
+ can_promote_members=True,
23
+ can_change_info=True,
24
+ can_post_messages=True, # Channels only
25
+ can_post_stories=True, # Channels only
26
+ can_edit_messages=True, # Channels only
27
+ can_edit_stories=True, # Channels only
28
+ can_invite_users=True,
29
+ can_pin_messages=True, # Groups and supergroups only
30
+ can_manage_topics=True, # Supergroups only
31
+ is_anonymous=True,
32
+ )
33
+
34
+ def __init__(self, ss_id: str):
35
+ self.u: UserClient = UserClient(ss_id, TG_API_ID, TG_API_HASH, storage_engine=PgStorage(ss_id))
36
+ self.b: BotClient = BotClient("6806432376", TG_API_ID, TG_API_HASH, storage_engine=PgStorage("6806432376"))
37
+
38
+ async def create_orders_forum(self, uid: str | int) -> tuple[int, bool]:
39
+ chat: Chat = await self.app.create_supergroup("Xync Orders", "Xync Orders")
40
+ if not (await self.app.toggle_forum_topics(chat_id=chat.id, enabled=True)):
41
+ await self.app.delete_channel(chat.id)
42
+ await chat.leave()
43
+ raise Exception(f"Chat {chat.id} for {self.app.me.username} not converted to forum")
44
+ await chat.add_members(["XyncNetBot"]) # , "xync_bot"
45
+ await chat.promote_member("XyncNetBot", self.max_privs)
46
+ added = await chat.add_members([uid])
47
+ try:
48
+ await sleep(1, await chat.get_member(uid))
49
+ except UserNotParticipant:
50
+ added = False
51
+ # await chat.leave()
52
+ return chat.id, added
53
+
54
+ async def get_user_photos(self, uid: str | int) -> Photos:
55
+ try:
56
+ peer = await self.app.resolve_peer(uid)
57
+ except Exception as e:
58
+ raise e
59
+ return await self.app.invoke(GetUserPhotos(user_id=peer, offset=0, limit=1, max_id=-1))
60
+
61
+
62
+ async def main():
63
+ _ = await init_db(PG_DSN, models, True)
64
+ user: models.User = await models.User.filter(status__gt=0).first()
65
+ bot = BotClient(TOKEN)
66
+ await bot.start()
67
+ uc = UserClient(str(user.username_id), bot)
68
+ await uc.start()
69
+ await uc.stop()
70
+ await bot.stop()
71
+
72
+
73
+ if __name__ == "__main__":
74
+ run(main())
@@ -0,0 +1,104 @@
1
+ from pyrogram import enums
2
+ from pyrogram.errors import BadRequest, SessionPasswordNeeded, AuthKeyUnregistered
3
+ from pyrogram.types import Message, User, SentCode
4
+
5
+ from xync_client.Pyrogram.base import BaseClient, AuthTopic
6
+ from xync_client.Pyrogram.bot import BotClient
7
+ from xync_client.loader import WSToken
8
+
9
+
10
+ class UserClient(BaseClient):
11
+ bot: BotClient
12
+
13
+ def __init__(self, name: str, bot: BotClient, *args, **kwargs):
14
+ super().__init__(name, *args, **kwargs)
15
+ self.bot = bot
16
+
17
+ async def ask_for(self, topic: AuthTopic, question: str) -> str:
18
+ await self.bot.send_message(self.storage.me_id, question)
19
+ self.bot.storage.session.state[self.storage.me_id] = {"waiting_for": topic}
20
+ return await self.bot.wait_auth_from(self.storage.me_id, topic)
21
+
22
+ async def send(self, txt: str) -> Message:
23
+ return await self.bot.send_message(self.storage.me_id, txt)
24
+
25
+ async def authorize(self, sent_code: SentCode = None) -> User:
26
+ sent_code_desc = {
27
+ enums.SentCodeType.APP: "Telegram app",
28
+ enums.SentCodeType.SMS: "SMS",
29
+ enums.SentCodeType.CALL: "phone call",
30
+ enums.SentCodeType.FLASH_CALL: "phone flash call",
31
+ enums.SentCodeType.FRAGMENT_SMS: "Fragment SMS",
32
+ enums.SentCodeType.EMAIL_CODE: "email code",
33
+ }
34
+ # Step 1: Phone
35
+ if not self.phone_number:
36
+ try:
37
+ self.phone_number = await self.ask_for("phone", "Your phone:")
38
+ if not self.phone_number:
39
+ await self.authorize()
40
+ sent_code = await self.send_code(self.phone_number)
41
+ except BadRequest as e:
42
+ await self.send(e.MESSAGE)
43
+ self.phone_number = None
44
+ return await self.authorize(sent_code)
45
+ # Step 2: Code
46
+ if not self.phone_code:
47
+ _ = await self.ask_for("code", f"The confirm code sent via {sent_code_desc[sent_code.type]}")
48
+ self.phone_code = _.replace("_", "")
49
+ try:
50
+ signed_in = await self.sign_in(self.phone_number, sent_code.phone_code_hash, self.phone_code)
51
+ except BadRequest as e:
52
+ await self.send(e.MESSAGE)
53
+ self.phone_code = None
54
+ return await self.authorize(sent_code)
55
+ except SessionPasswordNeeded as e:
56
+ # Step 2.1?: Cloud password
57
+ await self.send(e.MESSAGE)
58
+ while True:
59
+ self.password = await self.ask_for("pass", f"Enter pass: (hint: {await self.get_password_hint()})")
60
+ try:
61
+ return await self.check_password(self.password)
62
+ except BadRequest as e:
63
+ await self.send(e.MESSAGE)
64
+ self.password = None
65
+
66
+ if isinstance(signed_in, User):
67
+ await self.send("✅")
68
+ return signed_in
69
+
70
+ if not signed_in:
71
+ await self.send("No registered such phone number")
72
+
73
+
74
+ async def main():
75
+ from x_model import init_db
76
+ from xync_schema import models
77
+ from xync_client.loader import PG_DSN, TOKEN
78
+
79
+ _ = await init_db(PG_DSN, models, True)
80
+ _ = await models.Proxy.load_list(WSToken)
81
+ bot = BotClient(TOKEN)
82
+ await bot.start()
83
+ uc = UserClient(
84
+ "7049542242",
85
+ bot,
86
+ proxy=dict(
87
+ scheme="socks5", hostname="207.244.217.165", port=6712, username="hmxelnzd", password="zaw8ied2qdjc"
88
+ ),
89
+ )
90
+ try:
91
+ await uc.start()
92
+ except AuthKeyUnregistered as e:
93
+ print(e.MESSAGE)
94
+ await uc.send(e.MESSAGE)
95
+ await uc.storage.session.delete()
96
+ else:
97
+ await uc.stop()
98
+ await bot.stop()
99
+
100
+
101
+ if __name__ == "__main__":
102
+ from asyncio import run
103
+
104
+ run(main())
@@ -0,0 +1,43 @@
1
+ from asyncio import run
2
+ from urllib.parse import parse_qs
3
+
4
+ from pyrogram.raw import functions
5
+ from pyrogram.raw.types import InputPeerSelf
6
+ from x_model import init_db
7
+ from xync_client.Pyrogram.base import BaseClient
8
+
9
+ from xync_client.loader import PG_DSN
10
+ from xync_schema import models
11
+ from xync_schema.models import Actor
12
+
13
+
14
+ class PyroClient(BaseClient):
15
+ async def get_init_data(self) -> dict:
16
+ bot = await self.resolve_peer("wallet")
17
+ res = await self.invoke(functions.messages.RequestWebView(peer=InputPeerSelf(), bot=bot, platform="ios"))
18
+ raw = parse_qs(res.url)["tgWebAppUserId"][0].split("#tgWebAppData=")[1]
19
+ j = parse_qs(raw)
20
+ return {
21
+ "web_view_init_data": {
22
+ "query_id": j["query_id"][0],
23
+ "user": j["user"][0],
24
+ "auth_date": j["auth_date"][0],
25
+ "hash": j["hash"][0],
26
+ },
27
+ "web_view_init_data_raw": raw,
28
+ "ep": "menu",
29
+ }
30
+
31
+
32
+ async def main():
33
+ _ = await init_db(PG_DSN, models, True)
34
+ actor: Actor = (
35
+ await Actor.filter(agent__isnull=False, ex__name="TgWallet").prefetch_related("ex", "person__user").first()
36
+ )
37
+ pcl = PyroClient(actor.person.user.username_id)
38
+ await pcl.get_init_data()
39
+ ...
40
+
41
+
42
+ if __name__ == "__main__":
43
+ run(main())
@@ -1,4 +1,3 @@
1
- from aiogram import Bot, Dispatcher
2
1
  from dotenv import load_dotenv
3
2
  from os import getenv as env
4
3
 
@@ -10,8 +9,7 @@ if not (TOKEN := env("TOKEN")):
10
9
  load_dotenv("/api/.env")
11
10
  logging.info(TOKEN := env("TOKEN"))
12
11
 
13
- bot: Bot = Bot(token=TOKEN)
14
- dp: Dispatcher = Dispatcher()
15
12
  PG_DSN = f"postgres://{env('POSTGRES_USER')}:{env('POSTGRES_PASSWORD')}@{env('POSTGRES_HOST', 'xyncdbs')}:{env('POSTGRES_PORT', 5432)}/{env('POSTGRES_DB', env('POSTGRES_USER'))}"
16
13
  TG_API_ID = env("TG_API_ID")
17
14
  TG_API_HASH = env("TG_API_HASH")
15
+ WSToken = env("WST")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xync-client
3
- Version: 0.0.43.dev41
3
+ Version: 0.0.43.dev43
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
@@ -96,6 +96,11 @@ xync_client/Pms/Volet/__init__.py
96
96
  xync_client/Pms/Volet/api.py
97
97
  xync_client/Pms/Volet/_todo_req/req.mjs
98
98
  xync_client/Pms/Volet/_todo_req/req.py
99
+ xync_client/Pyrogram/base.py
100
+ xync_client/Pyrogram/bot.py
101
+ xync_client/Pyrogram/file.py
102
+ xync_client/Pyrogram/pyro.py
103
+ xync_client/Pyrogram/user.py
99
104
  xync_client/TgWallet/agent.py
100
105
  xync_client/TgWallet/asset.py
101
106
  xync_client/TgWallet/auth.py
@@ -1,147 +0,0 @@
1
- from asyncio import run, sleep
2
- from io import BytesIO
3
- from urllib.parse import parse_qs
4
-
5
- from aiogram import Bot
6
- from pyrogram import Client
7
- from pyrogram.errors import UserNotParticipant
8
- from pyrogram.raw import functions
9
- from pyrogram.raw.functions.messages import UploadMedia
10
- from pyrogram.raw.functions.photos import GetUserPhotos
11
- from pyrogram.raw.functions.upload import GetFile
12
- from pyrogram.raw.types import (
13
- InputPeerSelf,
14
- InputMediaUploadedDocument,
15
- MessageMediaDocument,
16
- InputMediaUploadedPhoto,
17
- MessageMediaPhoto,
18
- InputDocumentFileLocation,
19
- InputPhotoFileLocation,
20
- )
21
- from pyrogram.raw.types.photos import Photos
22
- from pyrogram.raw.types.upload import File
23
- from pyrogram.types import Chat, ChatPrivileges
24
- from x_model import init_db
25
- from xync_client.loader import TG_API_ID, TG_API_HASH, PG_DSN
26
- from xync_schema import models
27
- from xync_schema.models import Actor, PmAgent
28
-
29
-
30
- class PyroClient:
31
- max_privs = ChatPrivileges(
32
- can_manage_chat=True, # default
33
- can_delete_messages=True,
34
- can_delete_stories=True, # Channels only
35
- can_manage_video_chats=True, # Groups and supergroups only
36
- can_restrict_members=True,
37
- can_promote_members=True,
38
- can_change_info=True,
39
- can_post_messages=True, # Channels only
40
- can_post_stories=True, # Channels only
41
- can_edit_messages=True, # Channels only
42
- can_edit_stories=True, # Channels only
43
- can_invite_users=True,
44
- can_pin_messages=True, # Groups and supergroups only
45
- can_manage_topics=True, # Supergroups only
46
- is_anonymous=True,
47
- )
48
-
49
- def __init__(self, ab: Actor | PmAgent | Bot):
50
- name = str(ab.person.user.id) if isinstance(ab, Actor) else str(ab.id)
51
- auth = (
52
- {"bot_token": ab.token}
53
- if isinstance(ab, Bot)
54
- else {"session_string": ab.agent.auth["sess"] if isinstance(ab, Actor) else ab.auth["sess"]}
55
- )
56
- self.app: Client = Client(name, TG_API_ID, TG_API_HASH, **auth, in_memory=True)
57
-
58
- @staticmethod
59
- def ref_enc(ph_id: int, access_hash: int, ref: bytes) -> bytes:
60
- return ph_id.to_bytes(8, "big") + access_hash.to_bytes(8, "big", signed=True) + ref
61
-
62
- @staticmethod
63
- def ref_dec(full_ref: bytes) -> tuple[int, int, bytes]:
64
- pid, ah = int.from_bytes(full_ref[:8], "big"), int.from_bytes(full_ref[8:16], "big", signed=True)
65
- return pid, ah, full_ref[16:]
66
-
67
- async def get_init_data(self) -> dict:
68
- async with self.app:
69
- bot = await self.app.resolve_peer("wallet")
70
- res = await self.app.invoke(
71
- functions.messages.RequestWebView(peer=InputPeerSelf(), bot=bot, platform="ios")
72
- )
73
- raw = parse_qs(res.url)["tgWebAppUserId"][0].split("#tgWebAppData=")[1]
74
- j = parse_qs(raw)
75
- return {
76
- "web_view_init_data": {
77
- "query_id": j["query_id"][0],
78
- "user": j["user"][0],
79
- "auth_date": j["auth_date"][0],
80
- "hash": j["hash"][0],
81
- },
82
- "web_view_init_data_raw": raw,
83
- "ep": "menu",
84
- }
85
-
86
- async def create_orders_forum(self, uid: str | int) -> tuple[int, bool]:
87
- chat: Chat = await self.app.create_supergroup("Xync Orders", "Xync Orders")
88
- if not (await self.app.toggle_forum_topics(chat_id=chat.id, enabled=True)):
89
- await self.app.delete_channel(chat.id)
90
- await chat.leave()
91
- raise Exception(f"Chat {chat.id} for {self.app.me.username} not converted to forum")
92
- await chat.add_members(["XyncNetBot"]) # , "xync_bot"
93
- await chat.promote_member("XyncNetBot", self.max_privs)
94
- added = await chat.add_members([uid])
95
- try:
96
- await sleep(1, await chat.get_member(uid))
97
- except UserNotParticipant:
98
- added = False
99
- # await chat.leave()
100
- return chat.id, added
101
-
102
- async def get_user_photos(self, uid: str | int) -> Photos:
103
- try:
104
- peer = await self.app.resolve_peer(uid)
105
- except Exception as e:
106
- raise e
107
- return await self.app.invoke(GetUserPhotos(user_id=peer, offset=0, limit=1, max_id=-1))
108
-
109
- async def send_img(self, txt: str, byts: bytes, uid="me"):
110
- return await self.app.send_photo(uid, BytesIO(byts), txt)
111
-
112
- async def save_file(self, byts: bytes, ctype: str) -> tuple[MessageMediaDocument, bytes]:
113
- in_file = await self.app.save_file(BytesIO(byts))
114
- imud = InputMediaUploadedDocument(file=in_file, mime_type=ctype, attributes=[])
115
- upf: MessageMediaDocument = await self.app.invoke(UploadMedia(peer=InputPeerSelf(), media=imud))
116
- return upf, (
117
- upf.document.id.to_bytes(8, "big")
118
- + upf.document.access_hash.to_bytes(8, "big", signed=True)
119
- + upf.document.file_reference
120
- )
121
-
122
- async def save_photo(self, file: bytes) -> tuple[MessageMediaPhoto, bytes]:
123
- in_file = await self.app.save_file(BytesIO(file))
124
- upm = UploadMedia(peer=InputPeerSelf(), media=InputMediaUploadedPhoto(file=in_file))
125
- upp: MessageMediaPhoto = await self.app.invoke(upm)
126
- return upp, self.ref_enc(upp.photo.id, upp.photo.access_hash, upp.photo.file_reference)
127
-
128
- async def get_file(self, fid: bytes) -> File:
129
- pid, ah, ref = self.ref_dec(fid)
130
- loc = InputDocumentFileLocation(id=pid, access_hash=ah, file_reference=ref, thumb_size="x")
131
- return await self.app.invoke(GetFile(location=loc, offset=0, limit=512 * 1024))
132
-
133
- async def get_photo(self, fid: bytes, st: str) -> File:
134
- pid, ah, ref = self.ref_dec(fid)
135
- loc = InputPhotoFileLocation(id=pid, access_hash=ah, file_reference=ref, thumb_size=st)
136
- return await self.app.invoke(GetFile(location=loc, offset=0, limit=512 * 1024))
137
-
138
-
139
- async def main():
140
- _ = await init_db(PG_DSN, models, True)
141
- actor: Actor = await Actor.filter(agent__isnull=False, ex__name="TgWallet").prefetch_related("ex").first()
142
- pcl = PyroClient(actor)
143
- await pcl.create_orders_forum(actor.person.user.id)
144
-
145
-
146
- if __name__ == "__main__":
147
- run(main())