xync-client 0.0.25.dev111__tar.gz → 0.0.25.dev116__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 (98) hide show
  1. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/.gitignore +2 -1
  2. {xync_client-0.0.25.dev111/xync_client.egg-info → xync_client-0.0.25.dev116}/PKG-INFO +1 -1
  3. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Abc/Agent.py +2 -2
  4. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Abc/Base.py +2 -2
  5. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Abc/Ex.py +55 -34
  6. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Abc/InAgent.py +6 -1
  7. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Binance/ex.py +6 -3
  8. xync_client-0.0.25.dev116/xync_client/Bybit/InAgent.py +42 -0
  9. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Bybit/agent.py +32 -9
  10. xync_client-0.0.25.dev116/xync_client/Bybit/ws.py +68 -0
  11. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Mexc/ex.py +40 -23
  12. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/TgWallet/agent.py +1 -0
  13. xync_client-0.0.25.dev116/xync_client/TgWallet/pyro.py +144 -0
  14. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116/xync_client.egg-info}/PKG-INFO +1 -1
  15. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client.egg-info/SOURCES.txt +2 -0
  16. xync_client-0.0.25.dev111/xync_client/TgWallet/pyro.py +0 -156
  17. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/.env.sample +0 -0
  18. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/.pre-commit-config.yaml +0 -0
  19. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/README.md +0 -0
  20. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/makefile +0 -0
  21. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/pyproject.toml +0 -0
  22. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/setup.cfg +0 -0
  23. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/TestAgent.py +0 -0
  24. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/TestAsset.py +0 -0
  25. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/TestEx.py +0 -0
  26. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/TestOrder.py +0 -0
  27. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/_todo_refact/Binance/test_binance.py +0 -0
  28. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/_todo_refact/Bybit/test_bybit.py +0 -0
  29. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/_todo_refact/Bybit/test_bybit_p2p.py +0 -0
  30. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/_todo_refact/Gate/test_gate.py +0 -0
  31. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/_todo_refact/Htx/test_htx_p2p.py +0 -0
  32. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/_todo_refact/Wallet/test_agent.py +0 -0
  33. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/_todo_refact/Wallet/test_ex.py +0 -0
  34. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/_todo_refact/__init__.py +0 -0
  35. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/tests/_todo_refact/_test_ex.py +0 -0
  36. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Abc/Asset.py +0 -0
  37. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Abc/AuthTrait.py +0 -0
  38. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Abc/BaseTest.py +0 -0
  39. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Abc/Order.py +0 -0
  40. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Abc/types.py +0 -0
  41. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Binance/__init__.py +0 -0
  42. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Binance/binance_async.py +0 -0
  43. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Binance/earn_api.py +0 -0
  44. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Binance/etype/ad.py +0 -0
  45. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Binance/etype/pm.py +0 -0
  46. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Binance/exceptions.py +0 -0
  47. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Binance/sapi.py +0 -0
  48. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Binance/web_c2c.py +0 -0
  49. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BingX/__init__.py +0 -0
  50. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BingX/agent.py +0 -0
  51. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BingX/base.py +0 -0
  52. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BingX/etype/ad.py +0 -0
  53. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BingX/etype/pm.py +0 -0
  54. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BingX/ex.py +0 -0
  55. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BingX/req.mjs +0 -0
  56. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BingX/sign.js +0 -0
  57. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BitGet/__init__.py +0 -0
  58. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BitGet/agent.py +0 -0
  59. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BitGet/etype/ad.py +0 -0
  60. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BitGet/ex.py +0 -0
  61. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BitGet/req.mjs +0 -0
  62. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/BitPapa/ex.py +0 -0
  63. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Bybit/etype/ad.py +0 -0
  64. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Bybit/ex.py +0 -0
  65. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Bybit/web_earn.py +0 -0
  66. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Bybit/web_p2p.py +0 -0
  67. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Gate/etype/ad.py +0 -0
  68. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Gate/ex.py +0 -0
  69. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Gate/premarket.py +0 -0
  70. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Htx/agent.py +0 -0
  71. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Htx/earn.py +0 -0
  72. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Htx/etype/__init__.py +0 -0
  73. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Htx/etype/ad.py +0 -0
  74. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Htx/etype/cred.py +0 -0
  75. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Htx/etype/pm.py +0 -0
  76. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Htx/ex.py +0 -0
  77. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/KuCoin/etype/ad.py +0 -0
  78. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/KuCoin/etype/pm.py +0 -0
  79. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/KuCoin/ex.py +0 -0
  80. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/KuCoin/web.py +0 -0
  81. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Mexc/etype/ad.py +0 -0
  82. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Mexc/etype/pm.py +0 -0
  83. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Okx/etype/ad.py +0 -0
  84. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Okx/etype/pm.py +0 -0
  85. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/Okx/ex.py +0 -0
  86. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/TgWallet/asset.py +0 -0
  87. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/TgWallet/auth.py +0 -0
  88. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/TgWallet/ex.py +0 -0
  89. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/TgWallet/inAgent.py +0 -0
  90. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/TgWallet/order.py +0 -0
  91. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/TgWallet/pyd.py +0 -0
  92. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/TgWallet/web.py +0 -0
  93. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/__init__.py +0 -0
  94. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/loader.py +0 -0
  95. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client/pm_unifier.py +0 -0
  96. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client.egg-info/dependency_links.txt +0 -0
  97. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client.egg-info/requires.txt +0 -0
  98. {xync_client-0.0.25.dev111 → xync_client-0.0.25.dev116}/xync_client.egg-info/top_level.txt +0 -0
@@ -8,4 +8,5 @@ __pycache__
8
8
  /build
9
9
  .vscode
10
10
  /xync_client/Gate/res.js
11
- /tests/res.js
11
+ /tests/res.js
12
+ *.session*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xync-client
3
- Version: 0.0.25.dev111
3
+ Version: 0.0.25.dev116
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
@@ -11,9 +11,9 @@ from xync_client.Abc.types import CredExOut
11
11
 
12
12
 
13
13
  class BaseAgentClient(BaseClient):
14
- def __init__(self, agent: Agent):
14
+ def __init__(self, agent: Agent, headers: dict[str, str] = None, cookies: dict[str, str] = None):
15
15
  self.agent: Agent = agent
16
- super().__init__(self.agent.actor.ex) # , "host_p2p"
16
+ super().__init__(self.agent.actor.ex, "host_p2p", headers, cookies)
17
17
  self.ex_client: BaseExClient = self.ex.client()
18
18
 
19
19
  # 0: Получшение ордеров в статусе status, по монете coin, в валюте coin, в направлении is_sell: bool
@@ -10,6 +10,6 @@ MapOfIdsList = dict[int | str, list[int | str]]
10
10
  class BaseClient(Client):
11
11
  ex: Ex
12
12
 
13
- def __init__(self, ex: Ex, attr: str = "host_p2p"):
13
+ def __init__(self, ex: Ex, attr: str = "host_p2p", headers: dict[str, str] = None, cookies: dict[str, str] = None):
14
14
  self.ex = ex
15
- super().__init__(getattr(ex, attr))
15
+ super().__init__(getattr(ex, attr), headers, cookies)
@@ -1,10 +1,13 @@
1
1
  import logging
2
2
  from abc import abstractmethod
3
+ from asyncio import sleep
3
4
 
4
5
  import msgspec
6
+ from aiohttp import ClientSession
5
7
  from msgspec import Struct
6
8
  from tortoise.exceptions import MultipleObjectsReturned, IntegrityError
7
9
  from xync_schema import models
10
+ from xync_schema.enums import FileType
8
11
  from xync_schema.types import CurEx, CoinEx, BaseAd, BaseAdIn
9
12
 
10
13
  from xync_client.Abc.Base import BaseClient, MapOfIdsList
@@ -17,6 +20,11 @@ from xync_client.pm_unifier import PmUnifier, PmUni
17
20
  class BaseExClient(BaseClient):
18
21
  cur_map: dict[int, str] = {}
19
22
  unifier_class: type = PmUnifier
23
+ logo_map = {
24
+ "Binance": "bin.bnbstatic.com",
25
+ "Gate": "www.gate.io",
26
+ "Mexc": "www.mexc.com/api/file/download",
27
+ }
20
28
 
21
29
  @abstractmethod
22
30
  def pm_type_map(self, typ: models.Pmex) -> str: ...
@@ -66,7 +74,6 @@ class BaseExClient(BaseClient):
66
74
 
67
75
  # Импорт Pm-ов (с Pmcur-, Pmex- и Pmcurex-ами) и валют (с Curex-ами) с биржи в бд
68
76
  async def set_pmcurexs(self):
69
- PyroClient(bot)
70
77
  # Curs
71
78
  cur_pyds: dict[str, CurEx] = await self.curs()
72
79
  curs: dict[int | str, models.Cur] = {
@@ -121,69 +128,66 @@ class BaseExClient(BaseClient):
121
128
  # todo: curexcountry
122
129
 
123
130
  # Pms
124
- pms_epyds: dict[int | str, PmEx] = {
131
+ pmexs_epyds: dict[int | str, PmEx] = {
125
132
  k: v for k, v in sorted((await self.pms()).items(), key=lambda x: x[1].name)
126
133
  } # sort by name
127
134
  pms: dict[int | str, models.Pm] = dict({})
128
135
  prev = 0, "", "", None # id, normd-name, orig-name
129
136
  cntrs = [c.lower() for c in await models.Country.all().values_list("name", flat=True)]
130
137
  uni = self.unifier_class(cntrs)
131
- for k, pm in pms_epyds.items():
132
- pmu: PmUni = uni(pm.name)
138
+ for k, pmex in pmexs_epyds.items():
139
+ pmu: PmUni = uni(pmex.name)
133
140
  country_id = (
134
141
  await models.Country.get(name__iexact=cnt).values_list("id", flat=True)
135
142
  if (cnt := pmu.country)
136
143
  else None
137
144
  )
138
- if prev[2] == pm.name and pmu.country == prev[3]: # оригинальное имя не уникально на этой бирже
139
- logging.warning(f"Pm: '{pm.name}' duplicated with ids {prev[0]}: {k} on {self.ex.name}")
145
+ if prev[2] == pmex.name and pmu.country == prev[3]: # оригинальное имя не уникально на этой бирже
146
+ logging.warning(f"Pm: '{pmex.name}' duplicated with ids {prev[0]}: {k} on {self.ex.name}")
140
147
  # новый Pm не добавляем, а берем старый с этим названием
141
148
  pm_ = pms.get(prev[0], await models.Pm.get_or_none(norm=prev[1], country_id=country_id))
142
149
  # и добавляем Pmex для него
143
- await models.Pmex.update_or_create({"name": pm.name}, ex=self.ex, exid=k, pm=pm_)
150
+ await models.Pmex.update_or_create({"name": pmex.name}, ex=self.ex, exid=k, pm=pm_)
144
151
  elif (
145
152
  prev[1] == pmu.norm and pmu.country == prev[3]
146
153
  ): # 2 разных оригинальных имени на этой бирже совпали при нормализации
147
154
  logging.error(
148
- f"Pm: {pm.name}&{prev[2]} overnormd as {pmu.norm} with ids {prev[0]}: {k} on {self.ex.name}"
155
+ f"Pm: {pmex.name}&{prev[2]} overnormd as {pmu.norm} with ids {prev[0]}: {k} on {self.ex.name}"
149
156
  )
150
157
  # новый Pm не добавляем, только Pmex для него
151
158
  # новый Pm не добавляем, а берем старый с этим названием
152
159
  pm_ = pms.get(prev[0], await models.Pm.get_or_none(norm=prev[1], country_id=country_id))
153
160
  # и добавляем.обновляем Pmex для него
154
- await models.Pmex.update_or_create({"pm": pm_}, ex=self.ex, exid=k, name=pm.name)
161
+ await models.Pmex.update_or_create({"pm": pm_}, ex=self.ex, exid=k, name=pmex.name)
155
162
  else:
156
- pmin = models.Pm.validate({**pmu.model_dump(), "country_id": country_id, "typ": pm.typ})
157
- # # logo
158
- # if pm.logo and not await models.File.exists(name=pm.logo):
159
- # if not pm.logo.startswith("https:"):
160
- # if not pm.logo.startswith("/"):
161
- # pm.logo = "/" + pm.logo
162
- # pm.logo = "https://" + pm.logo
163
- # async with ClientSession() as ss:
164
- # resp = await ss.get(pm.logo)
165
- # if resp.ok:
166
- # byts = await resp.read()
167
- # upf, ref = await pyro.save_file(byts, resp.content_type)
168
- # await sleep(1)
169
- # typ = FileType[resp.content_type.split("/")[-1]]
170
- # file, _ = await models.File.update_or_create(
171
- # {"ref": ref, "size": len(byts), "typ": typ}, name=pm.logo
172
- # )
173
- # # fil = await pyro.get_file(file.ref) # check
174
- # pmin.logo_id = file.id
175
- # # /logo
163
+ pmin = models.Pm.validate({**pmu.model_dump(), "country_id": country_id, "typ": pmex.typ})
176
164
  try:
177
165
  pms[k], _ = await models.Pm.update_or_create(**pmin.df_unq())
178
166
  except (MultipleObjectsReturned, IntegrityError) as e:
179
167
  raise e
180
- prev = k, pmu.norm, pm.name, pmu.country
168
+ prev = k, pmu.norm, pmex.name, pmu.country
169
+
181
170
  # Pmexs
182
- pmexs = [models.Pmex(exid=k, ex=self.ex, pm=pm, name=pms_epyds[k].name) for k, pm in pms.items()]
183
- await models.Pmex.bulk_create(pmexs, on_conflict=["ex_id", "exid"], update_fields=["pm_id", "name", "name_"])
171
+ pbot = PyroClient(bot)
172
+ await pbot.app.start()
173
+ async with ClientSession(headers=getattr(self, "logo_headers", None)) as ss:
174
+ pmexs = [
175
+ models.Pmex(
176
+ # todo: refact logo
177
+ exid=k,
178
+ ex=self.ex,
179
+ pm=pm,
180
+ name=pmexs_epyds[k].name,
181
+ logo=await self.logo_save(pmexs_epyds[k].logo, pbot, ss),
182
+ )
183
+ for k, pm in pms.items()
184
+ ]
185
+ await pbot.app.stop()
186
+
187
+ await models.Pmex.bulk_create(pmexs, on_conflict=["ex_id", "exid"], update_fields=["pm_id", "logo_id", "name"])
184
188
  # Pmex banks
185
- for k, pm in pms_epyds.items():
186
- if banks := pm.banks:
189
+ for k, pmex in pmexs_epyds.items():
190
+ if banks := pmex.banks:
187
191
  pmex = await models.Pmex.get(ex=self.ex, exid=k) # pm=pms[k],
188
192
  for b in banks:
189
193
  await models.PmexBank.update_or_create({"name": b.name}, exid=b.exid, pmex=pmex)
@@ -203,6 +207,23 @@ class BaseExClient(BaseClient):
203
207
  # pmcurexs = [Pmcurex(pmcur=pmcur, ex=self.ex) for pmcur in pmcurs]
204
208
  # await Pmcurex.bulk_create(pmcurexs)
205
209
 
210
+ async def logo_save(self, url: str | None, pbot: PyroClient, ss: ClientSession) -> models.File | None:
211
+ if url or (file := None):
212
+ if not (file := await models.File.get_or_none(name=url)):
213
+ if not url.startswith("https:"):
214
+ if not url.startswith("/"):
215
+ url = "/" + url
216
+ url = "https://" + self.logo_map[self.ex.name] + url
217
+ resp = await ss.get(url)
218
+ if resp.ok:
219
+ byts = await resp.read()
220
+ upf, ref = await pbot.save_file(byts, resp.content_type)
221
+ await sleep(0.34)
222
+ typ = FileType[resp.content_type.split("/")[-1]]
223
+ file, _ = await models.File.update_or_create({"ref": ref, "size": len(byts), "typ": typ}, name=url)
224
+ # fr = await pbot.get_file(file.ref) # check
225
+ return file
226
+
206
227
  # Импорт монет (с Coinex-ами) с биржи в бд
207
228
  async def set_coinexs(self):
208
229
  coins: dict[str, CoinEx] = await self.coins()
@@ -1,8 +1,13 @@
1
1
  from abc import abstractmethod
2
2
 
3
+ from xync_schema.models import Agent
4
+
5
+ from xync_client.Abc.Agent import BaseAgentClient
6
+
3
7
 
4
8
  class BaseInAgentClient:
5
- def __init__(self): ...
9
+ def __init__(self, agent: Agent):
10
+ self.agent_client: BaseAgentClient = agent.client()
6
11
 
7
12
  @abstractmethod
8
13
  async def start_listen(self) -> bool: ...
@@ -22,7 +22,7 @@ class ExClient(BaseExClient):
22
22
  class BinanceUnifier(PmUnifier):
23
23
  pm_map = {
24
24
  "Banque de développement local (BDL)": "Banque de développement local (BDL)",
25
- "Viettel Money": "ViettelPay"
25
+ "Viettel Money": "ViettelPay",
26
26
  }
27
27
 
28
28
  unifier_class = BinanceUnifier
@@ -41,7 +41,10 @@ class ExClient(BaseExClient):
41
41
 
42
42
  async def curs(self) -> dict[int, types.CurEx]:
43
43
  curs = await self._post("/bapi/c2c/v1/friendly/c2c/trade-rule/fiat-list")
44
- return {c["currencyCode"]: types.CurEx(exid=c["currencyCode"], ticker=c["currencyCode"], scale=c["currencyScale"]) for c in curs["data"]}
44
+ return {
45
+ c["currencyCode"]: types.CurEx(exid=c["currencyCode"], ticker=c["currencyCode"], scale=c["currencyScale"])
46
+ for c in curs["data"]
47
+ }
45
48
 
46
49
  async def coins(self) -> dict[int, types.CoinEx]:
47
50
  for cur in (await self.curs()).keys():
@@ -131,10 +134,10 @@ async def main():
131
134
  _ = await init_db(PG_DSN, models)
132
135
  ex = await Ex.get(name="Binance")
133
136
  cl = ExClient(ex)
137
+ await cl.set_pmcurexs()
134
138
  await cl.pairs()
135
139
  await cl.pms()
136
140
  await cl.ads("ETH", "GEL", False)
137
- await cl.set_pmcurexs()
138
141
  await cl.close()
139
142
 
140
143
 
@@ -0,0 +1,42 @@
1
+ from asyncio import run
2
+
3
+ from x_model import init_db
4
+ from xync_schema import models
5
+
6
+ from xync_client.Abc.InAgent import BaseInAgentClient
7
+ from xync_client.Bybit.agent import AgentClient
8
+ from xync_client.Bybit.ws import prv
9
+ from xync_client.TgWallet.pyro import PyroClient
10
+ from xync_client.loader import PG_DSN, bot
11
+
12
+
13
+ class InAgentClient(BaseInAgentClient):
14
+ agent_client: AgentClient
15
+
16
+ async def start_listen(self):
17
+ t = await self.agent_client.ott()
18
+ ts = int(float(t["time_now"]) * 1000)
19
+ await prv(self.agent_client.agent.auth["deviceId"], t["result"], ts, listen)
20
+
21
+ # 3N: [T] - Уведомление об одобрении запроса на сделку
22
+ async def request_accepted_notify(self) -> int: ... # id
23
+
24
+
25
+ def listen(data: dict):
26
+ print(data)
27
+
28
+
29
+ async def main():
30
+ _ = await init_db(PG_DSN, models, True)
31
+ pbot = PyroClient(bot)
32
+ await pbot.app.start()
33
+ await pbot.app.create_channel("tc")
34
+ await pbot.app.stop()
35
+
36
+ agent = await models.Agent.filter(actor__ex_id=9, auth__isnull=False).prefetch_related("actor__ex").first()
37
+ cl: InAgentClient = agent.in_client()
38
+ await cl.agent_client.close()
39
+
40
+
41
+ if __name__ == "__main__":
42
+ run(main())
@@ -1,12 +1,16 @@
1
+ from asyncio import run
1
2
  from enum import IntEnum
2
3
  from time import sleep
3
4
 
4
5
  import pyotp
6
+ from x_model import init_db
7
+ from xync_schema import models
8
+
5
9
  from xync_client.Abc.Base import FlatDict
6
10
  from xync_schema.models import Cur
7
11
 
8
12
  from xync_client.Abc.Agent import BaseAgentClient
9
- from xync_client.loader import BYT2FA
13
+ from xync_client.loader import BYT2FA, PG_DSN
10
14
 
11
15
 
12
16
  class NoMakerException(Exception):
@@ -18,7 +22,7 @@ class AdsStatus(IntEnum):
18
22
  WORKING = 1
19
23
 
20
24
 
21
- class ExClient(BaseAgentClient): # Bybit client
25
+ class AgentClient(BaseAgentClient): # Bybit client
22
26
  host = "api2.bybit.com"
23
27
  headers = {"cookie": ";"} # rewrite token for public methods
24
28
 
@@ -109,31 +113,35 @@ class ExClient(BaseAgentClient): # Bybit client
109
113
  return fiat
110
114
  return list_methods[1]
111
115
 
116
+ async def ott(self):
117
+ t = await self._post("/user/private/ott")
118
+ return t
119
+
112
120
  # 27
113
- def fiat_upd(self, fiat_id: int, detail: str, name: str = None) -> dict:
121
+ async def fiat_upd(self, fiat_id: int, detail: str, name: str = None) -> dict:
114
122
  fiat = self.get_payment_method(fiat_id)
115
123
  fiat["realName"] = name
116
124
  fiat["accountNo"] = detail
117
125
  result = await self._post("/fiat/otc/user/payment/new_update", fiat)
118
126
  srt = result["result"]["securityRiskToken"]
119
- self._check_2fa(srt)
127
+ await self._check_2fa(srt)
120
128
  fiat["securityRiskToken"] = srt
121
129
  result2 = await self._post("/fiat/otc/user/payment/new_update", fiat)
122
130
  return result2
123
131
 
124
132
  # 28
125
- def fiat_del(self, fiat_id: int) -> dict:
133
+ async def fiat_del(self, fiat_id: int) -> dict | str:
126
134
  data = {"id": fiat_id, "securityRiskToken": ""}
127
135
  method = await self._post("/fiat/otc/user/payment/new_delete", data)
128
136
  srt = method["result"]["securityRiskToken"]
129
- self._check_2fa(srt)
137
+ await self._check_2fa(srt)
130
138
  data["securityRiskToken"] = srt
131
139
  delete = await self._post("/fiat/otc/user/payment/new_delete", data)
132
140
  return delete
133
141
 
134
- def switch_ads(self, new_status: AdsStatus) -> dict:
142
+ async def switch_ads(self, new_status: AdsStatus) -> dict:
135
143
  data = {"workStatus": new_status.name}
136
- res = self._post("/fiat/otc/maker/work-config/switch", data)
144
+ res = await self._post("/fiat/otc/maker/work-config/switch", data)
137
145
  return res
138
146
 
139
147
  def online_ads(self) -> str:
@@ -145,7 +153,7 @@ class ExClient(BaseAgentClient): # Bybit client
145
153
  ads = [ad for ad in list_ads if set(ad["payments"]) - {"5", "51"}]
146
154
  return float(ads[0]["price"])
147
155
 
148
- def my_fiats(self, cur: Cur = None):
156
+ async def my_fiats(self, cur: Cur = None):
149
157
  upm = await self._post("/fiat/otc/user/payment/list")
150
158
  return upm["result"]
151
159
 
@@ -296,3 +304,18 @@ class ExClient(BaseAgentClient): # Bybit client
296
304
  "size": 10,
297
305
  },
298
306
  )
307
+
308
+
309
+ def listen(data: dict):
310
+ print(data)
311
+
312
+
313
+ async def main():
314
+ _ = await init_db(PG_DSN, models, True)
315
+ agent = await models.Agent.filter(actor__ex_id=9, auth__isnull=False).prefetch_related("actor__ex").first()
316
+ cl: AgentClient = agent.client()
317
+ await cl.close()
318
+
319
+
320
+ if __name__ == "__main__":
321
+ run(main())
@@ -0,0 +1,68 @@
1
+ import hmac
2
+ import json
3
+ import websockets
4
+ from time import time
5
+
6
+
7
+ async def pub():
8
+ async with websockets.connect("wss://stream.bybit.com/v5/public/spot") as websocket:
9
+ sub_msg = json.dumps(
10
+ {"op": "subscribe", "req_id": "1", "args": ["tickers.BTCUSDT", "tickers.ETHUSDT", "tickers.TONUSDT"]}
11
+ )
12
+ await websocket.send(sub_msg)
13
+ p = {}
14
+ while resp := await websocket.recv():
15
+ if data := json.loads(resp).get("data"):
16
+ p[data["symbol"]] = data["lastPrice"]
17
+ print(f"BTC: {p.get('BTCUSDT')}\nETH: {p.get('ETHUSDT')}\nTON: {p.get('TONUSDT')}", end="\033[F\033[F")
18
+
19
+
20
+ async def priv(key: str, sec: str):
21
+ async with websockets.connect("wss://stream.bybit.com/v5/private") as websocket:
22
+ expires = int((time() + 5) * 1000)
23
+ # Generate signature.
24
+ signature = str(
25
+ hmac.new(bytes(sec, "utf-8"), bytes(f"GET/realtime{expires}", "utf-8"), digestmod="sha256").hexdigest()
26
+ )
27
+ auth_msg = json.dumps({"op": "auth", "args": [key, expires, signature]})
28
+ await websocket.send(auth_msg)
29
+ await websocket.send(json.dumps({"req_id": "100001", "op": "ping"}))
30
+ sub_msg = json.dumps({"op": "subscribe", "args": ["wallet"]})
31
+ await websocket.send(sub_msg)
32
+ while resp := await websocket.recv():
33
+ if data := json.loads(resp).get("data"):
34
+ print(data)
35
+
36
+
37
+ async def prv(did: str, tok: str, ts: int, cb: callable):
38
+ u = f"wss://ws2.bybit.com/private?appid=bybit&os=web&deviceid={did}&timestamp={ts}"
39
+ async with websockets.connect(u) as websocket:
40
+ auth_msg = json.dumps({"req_id": did, "op": "login", "args": [tok]})
41
+ await websocket.send(auth_msg)
42
+
43
+ sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
44
+ await websocket.send(sub_msg)
45
+ sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"SUPER_DEAL"}']})
46
+ await websocket.send(sub_msg)
47
+ sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"OTC_ORDER_STATUS"}']})
48
+ await websocket.send(sub_msg)
49
+ sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"WEB_THREE_SELL"}']})
50
+ await websocket.send(sub_msg)
51
+ sub_msg = json.dumps({"op": "input", "args": ["FIAT_OTC_TOPIC", '{"topic":"APPEALED_CHANGE"}']})
52
+ await websocket.send(sub_msg)
53
+
54
+ sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order"]})
55
+ await websocket.send(sub_msg)
56
+ sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order-eftd-complete-privilege-event"]})
57
+ await websocket.send(sub_msg)
58
+ sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.cashier.order-savings-product-event"]})
59
+ await websocket.send(sub_msg)
60
+ sub_msg = json.dumps({"op": "subscribe", "args": ["fiat.deal-core.order-savings-complete-event"]})
61
+ await websocket.send(sub_msg)
62
+
63
+ sub_msg = json.dumps({"op": "subscribe", "args": ["FIAT_OTC_TOPIC", "FIAT_OTC_ONLINE_TOPIC"]})
64
+ await websocket.send(sub_msg)
65
+
66
+ while resp := await websocket.recv():
67
+ if data := json.loads(resp).get("data"):
68
+ cb(data)
@@ -13,24 +13,39 @@ from xync_schema import types
13
13
  from xync_schema import models
14
14
  from xync_schema.models import Ex
15
15
 
16
+
16
17
  class ExClient(BaseExClient):
18
+ logo_headers = {
19
+ "accept-language": "ru,en;q=0.9",
20
+ "priority": "u=0, i",
21
+ "sec-ch-ua": '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"',
22
+ "sec-ch-ua-mobile": "?0",
23
+ "sec-ch-ua-platform": '"macOS"',
24
+ "sec-fetch-site": "none", # work from CURL, not work from aiohttp with no this header
25
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
26
+ }
27
+
17
28
  class MexcUnifier(PmUnifier):
18
29
  pm_map = {
19
- "Philippine National Bank (PNB)": "Philippines National Bank (PNB)",
20
- "Al-Rajhi Bank": "Al Rajhi Bank",
21
- "SBP - Fast Bank Transfer": "SBP",
22
- "Touch 'n Go": "Touch n Go"
30
+ "Philippine National Bank (PNB)": "Philippines National Bank (PNB)",
31
+ "Al-Rajhi Bank": "Al Rajhi Bank",
32
+ "SBP - Fast Bank Transfer": "SBP",
33
+ "Touch 'n Go": "Touch n Go",
23
34
  }
35
+
24
36
  unifier_class = MexcUnifier
25
37
 
26
38
  async def _pms(self, cur) -> list[pm.PmE]:
27
- pms = requests.get("https://p2p.mexc.com/api/payment/method", params={'currency': cur}).json()
39
+ pms = requests.get("https://p2p.mexc.com/api/payment/method", params={"currency": cur}).json()
28
40
  return [pm.PmE(**_pm) for _pm in pms["data"]]
29
41
 
30
42
  # 19: Список поддерживаемых валют тейкера
31
43
  async def curs(self) -> dict[str, types.CurEx]: # {cur.ticker: cur}
32
- _curs = requests.get('https://p2p.mexc.com/api/common/currency').json()
33
- return {cur["currency"]: types.CurEx(exid=cur["currency"], ticker=cur["currency"], scale=cur["scale"]) for cur in _curs["data"]}
44
+ _curs = requests.get("https://p2p.mexc.com/api/common/currency").json()
45
+ return {
46
+ cur["currency"]: types.CurEx(exid=cur["currency"], ticker=cur["currency"], scale=cur["scale"])
47
+ for cur in _curs["data"]
48
+ }
34
49
 
35
50
  # 20: Список платежных методов
36
51
  async def pms(self, cur: models.Cur = None) -> dict[int | str, PmEx]: # {pm.exid: pm}
@@ -47,8 +62,11 @@ class ExClient(BaseExClient):
47
62
 
48
63
  # 22: Список торгуемых монет (с ограничениям по валютам, если есть)
49
64
  async def coins(self) -> dict[str, types.CoinEx]: # {coin.ticker: coin}
50
- coins = requests.get('https://p2p.mexc.com/api/common/coins').json()
51
- return {coin["coinId"]:types.CoinEx(exid=coin["coinId"], ticker=coin["coinName"],scale=coin["quantityScale"]) for coin in coins["data"]}
65
+ coins = requests.get("https://p2p.mexc.com/api/common/coins").json()
66
+ return {
67
+ coin["coinId"]: types.CoinEx(exid=coin["coinId"], ticker=coin["coinName"], scale=coin["quantityScale"])
68
+ for coin in coins["data"]
69
+ }
52
70
 
53
71
  # 23: Список пар валюта/монет
54
72
  async def pairs(self) -> tuple[MapOfIdsList, MapOfIdsList]:
@@ -59,27 +77,26 @@ class ExClient(BaseExClient):
59
77
 
60
78
  # 24: Список объяв по (buy/sell, cur, coin, pm)
61
79
  async def ads(
62
- self, coin_exid: str, cur_exid: str, is_sell: bool, pm_exids: list[str | int] = None, amount: int = None
80
+ self, coin_exid: str, cur_exid: str, is_sell: bool, pm_exids: list[str | int] = None, amount: int = None
63
81
  ) -> list[ad.Ad]: # {ad.id: ad}
64
82
  params = {
65
- 'allowTrade': 'false',
66
- 'amount': amount or "",
67
- 'blockTrade': 'false',
68
- 'coinId': coin_exid,
69
- 'countryCode': '',
70
- 'currency': cur_exid,
71
- 'follow': 'false',
72
- 'haveTrade': 'false',
73
- 'page': '1',
74
- 'payMethod': pm_exids or "",
75
- 'tradeType': 'SELL' if is_sell else "BUY",
83
+ "allowTrade": "false",
84
+ "amount": amount or "",
85
+ "blockTrade": "false",
86
+ "coinId": coin_exid,
87
+ "countryCode": "",
88
+ "currency": cur_exid,
89
+ "follow": "false",
90
+ "haveTrade": "false",
91
+ "page": "1",
92
+ "payMethod": pm_exids or "",
93
+ "tradeType": "SELL" if is_sell else "BUY",
76
94
  }
77
95
 
78
96
  ads = requests.get("https://p2p.mexc.com/api/market", params=params).json()
79
97
  return [ad.Ad(**_ad) for _ad in ads["data"]]
80
98
 
81
99
 
82
-
83
100
  async def main():
84
101
  _ = await init_db(PG_DSN, models, True)
85
102
  ex = await Ex.get(name="Mexc")
@@ -93,6 +110,6 @@ async def main():
93
110
  _pms = await cl.pms()
94
111
  await cl.close()
95
112
 
113
+
96
114
  if __name__ == "__main__":
97
115
  run(main())
98
-
@@ -44,6 +44,7 @@ class Exceptions(StrEnum):
44
44
  CUR = "CURRENCY_IS_NOT_SUPPORTED_BY_PAYMENT_METHOD"
45
45
  MAX_ADS = "ACTIVE_OFFER_COUNT_LIMIT_REACHED"
46
46
  INACTIVE_AD = "OFFER_ILLEGAL_STATE"
47
+ FF = "CANNOT_DELETE_WHEN_USED_IN_OFFER"
47
48
 
48
49
 
49
50
  # class Status(IntEnum):
@@ -0,0 +1,144 @@
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 Agent
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: Agent | Bot):
50
+ name = str(ab.actor.person.user.id) if isinstance(ab, Agent) else str(ab.id)
51
+ auth = {"session_string": ab.auth["sess"]} if isinstance(ab, Agent) else {"bot_token": ab.token}
52
+ self.app: Client = Client(name, TG_API_ID, TG_API_HASH, **auth)
53
+
54
+ @staticmethod
55
+ def ref_enc(ph_id: int, access_hash: int, ref: bytes) -> bytes:
56
+ return ph_id.to_bytes(8, "big") + access_hash.to_bytes(8, "big", signed=True) + ref
57
+
58
+ @staticmethod
59
+ def ref_dec(full_ref: bytes) -> tuple[int, int, bytes]:
60
+ pid, ah = int.from_bytes(full_ref[:8], "big"), int.from_bytes(full_ref[8:16], "big", signed=True)
61
+ return pid, ah, full_ref[16:]
62
+
63
+ async def get_init_data(self) -> dict:
64
+ bot = await self.app.resolve_peer("wallet")
65
+ res = await self.app.invoke(functions.messages.RequestWebView(peer=InputPeerSelf(), bot=bot, platform="ios"))
66
+ raw = parse_qs(res.url)["tgWebAppUserId"][0].split("#tgWebAppData=")[1]
67
+ j = parse_qs(raw)
68
+ return {
69
+ "web_view_init_data": {
70
+ "query_id": j["query_id"][0],
71
+ "user": j["user"][0],
72
+ "auth_date": j["auth_date"][0],
73
+ "hash": j["hash"][0],
74
+ },
75
+ "web_view_init_data_raw": raw,
76
+ "ep": "menu",
77
+ }
78
+
79
+ async def create_orders_forum(self, uid: str | int) -> tuple[int, bool]:
80
+ await self.app.get_me()
81
+ chat: Chat = await self.app.create_supergroup("Xync Orders", "Xync Orders")
82
+ if not (await self.app.toggle_forum_topics(chat_id=chat.id, enabled=True)):
83
+ await self.app.delete_channel(chat.id)
84
+ await chat.leave()
85
+ raise Exception(f"Chat {chat.id} for {self.app.me.username} not converted to forum")
86
+ await chat.add_members(["XyncNetBot"]) # , "xync_bot"
87
+ await chat.promote_member("XyncNetBot", self.max_privs)
88
+ added = await chat.add_members([uid])
89
+ try:
90
+ await sleep(1, await chat.get_member(uid))
91
+ except UserNotParticipant:
92
+ added = False
93
+ # await chat.leave()
94
+ return chat.id, added
95
+
96
+ async def get_user_photos(self, uid: str | int) -> Photos:
97
+ try:
98
+ peer = await self.app.resolve_peer(uid)
99
+ except Exception as e:
100
+ raise e
101
+ return await self.app.invoke(GetUserPhotos(user_id=peer, offset=0, limit=1, max_id=-1))
102
+
103
+ async def send(self, uid, txt):
104
+ try:
105
+ return await self.app.send_message(uid, txt)
106
+ except Exception as e:
107
+ raise e
108
+
109
+ async def save_file(self, byts: bytes, ctype: str) -> tuple[MessageMediaDocument, bytes]:
110
+ in_file = await self.app.save_file(BytesIO(byts))
111
+ imud = InputMediaUploadedDocument(file=in_file, mime_type=ctype, attributes=[])
112
+ upf: MessageMediaDocument = await self.app.invoke(UploadMedia(peer=InputPeerSelf(), media=imud))
113
+ return upf, (
114
+ upf.document.id.to_bytes(8, "big")
115
+ + upf.document.access_hash.to_bytes(8, "big", signed=True)
116
+ + upf.document.file_reference
117
+ )
118
+
119
+ async def save_photo(self, file: bytes) -> tuple[MessageMediaPhoto, bytes]:
120
+ in_file = await self.app.save_file(BytesIO(file))
121
+ upm = UploadMedia(peer=InputPeerSelf(), media=InputMediaUploadedPhoto(file=in_file))
122
+ upp: MessageMediaPhoto = await self.app.invoke(upm)
123
+ return upp, self.ref_enc(upp.photo.id, upp.photo.access_hash, upp.photo.file_reference)
124
+
125
+ async def get_file(self, fid: bytes) -> File:
126
+ pid, ah, ref = self.ref_dec(fid)
127
+ loc = InputDocumentFileLocation(id=pid, access_hash=ah, file_reference=ref, thumb_size="x")
128
+ return await self.app.invoke(GetFile(location=loc, offset=0, limit=512 * 1024))
129
+
130
+ async def get_photo(self, fid: bytes, st: str) -> File:
131
+ pid, ah, ref = self.ref_dec(fid)
132
+ loc = InputPhotoFileLocation(id=pid, access_hash=ah, file_reference=ref, thumb_size=st)
133
+ return await self.app.invoke(GetFile(location=loc, offset=0, limit=512 * 1024))
134
+
135
+
136
+ async def main():
137
+ _ = await init_db(PG_DSN, models, True)
138
+ agent: Agent = await Agent.filter(auth__isnull=False, ex__name="TgWallet").prefetch_related("ex").first()
139
+ pcl = PyroClient(agent)
140
+ await pcl.create_orders_forum(agent.actor.user_id)
141
+
142
+
143
+ if __name__ == "__main__":
144
+ run(main())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xync-client
3
- Version: 0.0.25.dev111
3
+ Version: 0.0.25.dev116
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
@@ -57,10 +57,12 @@ xync_client/BitGet/ex.py
57
57
  xync_client/BitGet/req.mjs
58
58
  xync_client/BitGet/etype/ad.py
59
59
  xync_client/BitPapa/ex.py
60
+ xync_client/Bybit/InAgent.py
60
61
  xync_client/Bybit/agent.py
61
62
  xync_client/Bybit/ex.py
62
63
  xync_client/Bybit/web_earn.py
63
64
  xync_client/Bybit/web_p2p.py
65
+ xync_client/Bybit/ws.py
64
66
  xync_client/Bybit/etype/ad.py
65
67
  xync_client/Gate/ex.py
66
68
  xync_client/Gate/premarket.py
@@ -1,156 +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.base.upload import File
10
- from pyrogram.raw.functions.messages import UploadMedia
11
- from pyrogram.raw.functions.photos import GetUserPhotos
12
- from pyrogram.raw.functions.upload import GetFile
13
- from pyrogram.raw.types import (
14
- InputPeerSelf,
15
- InputMediaUploadedDocument,
16
- MessageMediaDocument,
17
- InputMediaUploadedPhoto,
18
- MessageMediaPhoto,
19
- InputDocumentFileLocation,
20
- InputPhotoFileLocation,
21
- )
22
- from pyrogram.raw.types.photos import Photos
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 Agent
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: Agent | Bot):
50
- name = str(ab.actor.person.user.id) if isinstance(ab, Agent) else str(ab.id)
51
- auth = {"session_string": ab.auth["sess"]} if isinstance(ab, Agent) else {"bot_token": ab.token}
52
- self.app: Client = Client(name, TG_API_ID, TG_API_HASH, **auth)
53
-
54
- async def get_init_data(self) -> dict:
55
- async with self.app as app:
56
- app: Client
57
- bot = await app.resolve_peer("wallet")
58
- res = await app.invoke(functions.messages.RequestWebView(peer=InputPeerSelf(), bot=bot, platform="ios"))
59
- raw = parse_qs(res.url)["tgWebAppUserId"][0].split("#tgWebAppData=")[1]
60
- j = parse_qs(raw)
61
- return {
62
- "web_view_init_data": {
63
- "query_id": j["query_id"][0],
64
- "user": j["user"][0],
65
- "auth_date": j["auth_date"][0],
66
- "hash": j["hash"][0],
67
- },
68
- "web_view_init_data_raw": raw,
69
- "ep": "menu",
70
- }
71
-
72
- async def create_orders_forum(self, uid: str | int) -> tuple[int, bool]:
73
- async with self.app as app:
74
- app: Client
75
- await app.get_me()
76
- chat: Chat = await app.create_supergroup("Xync Orders", "Xync Orders")
77
- if not (await app.toggle_forum_topics(chat_id=chat.id, enabled=True)):
78
- await app.delete_channel(chat.id)
79
- await chat.leave()
80
- raise Exception(f"Chat {chat.id} for {app.me.username} not converted to forum")
81
- await chat.add_members(["XyncNetBot"]) # , "xync_bot"
82
- await chat.promote_member("XyncNetBot", self.max_privs)
83
- added = await chat.add_members([uid])
84
- try:
85
- await sleep(1, await chat.get_member(uid))
86
- except UserNotParticipant:
87
- added = False
88
- # await chat.leave()
89
- return chat.id, added
90
-
91
- async def get_user_photos(self, uid: str | int) -> Photos:
92
- async with self.app as app:
93
- app: Client
94
- try:
95
- peer = await app.resolve_peer(uid)
96
- except Exception as e:
97
- raise e
98
- return await app.invoke(GetUserPhotos(user_id=peer, offset=0, limit=1, max_id=-1))
99
-
100
- async def send_message(self, uid, txt):
101
- async with self.app as app:
102
- app: Client
103
- try:
104
- return await app.send_message(uid, txt)
105
- except Exception as e:
106
- raise e
107
-
108
- async def save_file(self, byts: bytes, ctype: str) -> tuple[MessageMediaDocument, bytes]:
109
- async with self.app as app:
110
- in_file = await app.save_file(BytesIO(byts))
111
- imud = InputMediaUploadedDocument(file=in_file, mime_type=ctype, attributes=[])
112
- upf: MessageMediaDocument = await app.invoke(UploadMedia(peer=InputPeerSelf(), media=imud))
113
- return upf, (
114
- upf.document.id.to_bytes(8, "big")
115
- + upf.document.access_hash.to_bytes(8, "big", signed=True)
116
- + upf.document.file_reference
117
- )
118
-
119
- @staticmethod
120
- def ref_enc(ph_id: int, access_hash: int, ref: bytes) -> bytes:
121
- return ph_id.to_bytes(8, "big") + access_hash.to_bytes(8, "big", signed=True) + ref
122
-
123
- @staticmethod
124
- def ref_dec(full_ref: bytes) -> tuple[int, int, bytes]:
125
- pid, ah = int.from_bytes(full_ref[:8], "big"), int.from_bytes(full_ref[8:16], "big", signed=True)
126
- return pid, ah, full_ref[16:]
127
-
128
- async def save_photo(self, file: bytes) -> tuple[MessageMediaPhoto, bytes]:
129
- async with self.app as app:
130
- in_file = await app.save_file(BytesIO(file))
131
- upm = UploadMedia(peer=InputPeerSelf(), media=InputMediaUploadedPhoto(file=in_file))
132
- upp: MessageMediaPhoto = await app.invoke(upm)
133
- return upp, self.ref_enc(upp.photo.id, upp.photo.access_hash, upp.photo.file_reference)
134
-
135
- async def get_file(self, fid: bytes) -> File:
136
- async with self.app as app:
137
- pid, ah, ref = self.ref_dec(fid)
138
- loc = InputDocumentFileLocation(id=pid, access_hash=ah, file_reference=ref, thumb_size="x")
139
- return await app.invoke(GetFile(location=loc, offset=0, limit=512 * 1024))
140
-
141
- async def get_photo(self, fid: bytes, st: str) -> File:
142
- async with self.app as app:
143
- pid, ah, ref = self.ref_dec(fid)
144
- loc = InputPhotoFileLocation(id=pid, access_hash=ah, file_reference=ref, thumb_size=st)
145
- return await app.invoke(GetFile(location=loc, offset=0, limit=512 * 1024))
146
-
147
-
148
- async def main():
149
- _ = await init_db(PG_DSN, models, True)
150
- agent: Agent = await Agent.filter(auth__isnull=False, ex__name="TgWallet").prefetch_related("ex").first()
151
- pcl = PyroClient(agent)
152
- await pcl.create_orders_forum(agent.actor.user_id)
153
-
154
-
155
- if __name__ == "__main__":
156
- run(main())