CliRemote 1.6.0__tar.gz → 1.6.1__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 (46) hide show
  1. {cliremote-1.6.0 → cliremote-1.6.1}/CliRemote.egg-info/PKG-INFO +1 -1
  2. {cliremote-1.6.0 → cliremote-1.6.1}/PKG-INFO +1 -1
  3. {cliremote-1.6.0 → cliremote-1.6.1}/pyproject.toml +1 -1
  4. {cliremote-1.6.0 → cliremote-1.6.1}/remote/client_manager.py +16 -0
  5. cliremote-1.6.1/remote/client_picker.py +40 -0
  6. {cliremote-1.6.0 → cliremote-1.6.1}/setup.py +1 -1
  7. cliremote-1.6.0/remote/client_picker.py +0 -109
  8. {cliremote-1.6.0 → cliremote-1.6.1}/CliRemote.egg-info/SOURCES.txt +0 -0
  9. {cliremote-1.6.0 → cliremote-1.6.1}/CliRemote.egg-info/dependency_links.txt +0 -0
  10. {cliremote-1.6.0 → cliremote-1.6.1}/CliRemote.egg-info/requires.txt +0 -0
  11. {cliremote-1.6.0 → cliremote-1.6.1}/CliRemote.egg-info/top_level.txt +0 -0
  12. {cliremote-1.6.0 → cliremote-1.6.1}/LICENSE +0 -0
  13. {cliremote-1.6.0 → cliremote-1.6.1}/MANIFEST.in +0 -0
  14. {cliremote-1.6.0 → cliremote-1.6.1}/README.md +0 -0
  15. {cliremote-1.6.0 → cliremote-1.6.1}/remote/__init__.py +0 -0
  16. {cliremote-1.6.0 → cliremote-1.6.1}/remote/account_manager.py +0 -0
  17. {cliremote-1.6.0 → cliremote-1.6.1}/remote/account_viewer.py +0 -0
  18. {cliremote-1.6.0 → cliremote-1.6.1}/remote/admin_manager.py +0 -0
  19. {cliremote-1.6.0 → cliremote-1.6.1}/remote/analytics_manager.py +0 -0
  20. {cliremote-1.6.0 → cliremote-1.6.1}/remote/batch_manager.py +0 -0
  21. {cliremote-1.6.0 → cliremote-1.6.1}/remote/block_manager.py +0 -0
  22. {cliremote-1.6.0 → cliremote-1.6.1}/remote/caption_manager.py +0 -0
  23. {cliremote-1.6.0 → cliremote-1.6.1}/remote/cleaner.py +0 -0
  24. {cliremote-1.6.0 → cliremote-1.6.1}/remote/config.py +0 -0
  25. {cliremote-1.6.0 → cliremote-1.6.1}/remote/device_manager.py +0 -0
  26. {cliremote-1.6.0 → cliremote-1.6.1}/remote/file_sender.py +0 -0
  27. {cliremote-1.6.0 → cliremote-1.6.1}/remote/getcode_controller.py +0 -0
  28. {cliremote-1.6.0 → cliremote-1.6.1}/remote/health.py +0 -0
  29. {cliremote-1.6.0 → cliremote-1.6.1}/remote/help_menu.py +0 -0
  30. {cliremote-1.6.0 → cliremote-1.6.1}/remote/init.py +0 -0
  31. {cliremote-1.6.0 → cliremote-1.6.1}/remote/join_controller.py +0 -0
  32. {cliremote-1.6.0 → cliremote-1.6.1}/remote/joiner.py +0 -0
  33. {cliremote-1.6.0 → cliremote-1.6.1}/remote/leave_controller.py +0 -0
  34. {cliremote-1.6.0 → cliremote-1.6.1}/remote/lefter.py +0 -0
  35. {cliremote-1.6.0 → cliremote-1.6.1}/remote/mention_manager.py +0 -0
  36. {cliremote-1.6.0 → cliremote-1.6.1}/remote/precise_engine.py +0 -0
  37. {cliremote-1.6.0 → cliremote-1.6.1}/remote/profile_info.py +0 -0
  38. {cliremote-1.6.0 → cliremote-1.6.1}/remote/profile_media.py +0 -0
  39. {cliremote-1.6.0 → cliremote-1.6.1}/remote/profile_privacy.py +0 -0
  40. {cliremote-1.6.0 → cliremote-1.6.1}/remote/spammer.py +0 -0
  41. {cliremote-1.6.0 → cliremote-1.6.1}/remote/speed_manager.py +0 -0
  42. {cliremote-1.6.0 → cliremote-1.6.1}/remote/stop_manager.py +0 -0
  43. {cliremote-1.6.0 → cliremote-1.6.1}/remote/text_manager.py +0 -0
  44. {cliremote-1.6.0 → cliremote-1.6.1}/remote/username_manager.py +0 -0
  45. {cliremote-1.6.0 → cliremote-1.6.1}/remote/utils/sqlite_utils.py +0 -0
  46. {cliremote-1.6.0 → cliremote-1.6.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: CliRemote
3
- Version: 1.6.0
3
+ Version: 1.6.1
4
4
  Summary: Remote client framework for Telegram automation using Pyrogram
5
5
  Home-page: https://github.com/MohammadAhmadi-R/CliRemote
6
6
  Author: MrAhmadiRad
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: CliRemote
3
- Version: 1.6.0
3
+ Version: 1.6.1
4
4
  Summary: Remote client framework for Telegram automation using Pyrogram
5
5
  Home-page: https://github.com/MohammadAhmadi-R/CliRemote
6
6
  Author: MrAhmadiRad
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "CliRemote"
7
- version = "1.6.0"
7
+ version = "1.6.1"
8
8
  description = "Remote client framework for Telegram automation using Pyrogram"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -229,3 +229,19 @@ def accounts() -> List[str]:
229
229
 
230
230
  def get_active_accounts() -> Set[str]:
231
231
  return set(accounts())
232
+
233
+
234
+ def remove_client_from_pool(phone_number: str) -> None:
235
+ """
236
+ کلاینت را از pool خارج و stop می‌کند (به‌صورت غیرمسدودکننده).
237
+ """
238
+ cli = client_pool.get(phone_number)
239
+ if cli is not None:
240
+ try:
241
+ asyncio.create_task(cli.stop())
242
+ logger.info("%s: scheduled stop()", phone_number)
243
+ except Exception as e:
244
+ logger.warning("%s: stop() scheduling error: %s: %s", phone_number, type(e).__name__, e)
245
+ client_pool.pop(phone_number, None)
246
+ client_locks.pop(phone_number, None)
247
+ logger.info("%s: removed from client_pool and client_locks", phone_number)
@@ -0,0 +1,40 @@
1
+ import random
2
+ import logging
3
+ from typing import Optional
4
+ from .client_manager import client_pool, get_active_accounts, get_or_start_client
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ async def get_any_client(message=None) -> Optional[object]:
9
+ """
10
+ یک کلاینت آماده برمی‌گرداند:
11
+ 1) اگر کلاینت متصل در pool هست، همان را برمی‌گرداند.
12
+ 2) وگرنه از بین active_accounts یکی را استارت می‌کند.
13
+ """
14
+ # Use any connected client
15
+ for phone, cli in list(client_pool.items()):
16
+ try:
17
+ if getattr(cli, "is_connected", False):
18
+ logger.debug("get_any_client: using connected client %s", phone)
19
+ return cli
20
+ except Exception:
21
+ pass
22
+
23
+ # Start one if needed
24
+ accs = list(get_active_accounts())
25
+ if not accs:
26
+ logger.warning("get_any_client: no active accounts")
27
+ return None
28
+
29
+ random.shuffle(accs)
30
+ for phone in accs:
31
+ try:
32
+ cli = await get_or_start_client(phone)
33
+ if cli and getattr(cli, "is_connected", False):
34
+ logger.info("get_any_client: started %s", phone)
35
+ return cli
36
+ except Exception as e:
37
+ logger.warning("get_any_client: failed start %s: %s: %s", phone, type(e).__name__, e)
38
+
39
+ logger.error("get_any_client: could not get any client")
40
+ return None
@@ -5,7 +5,7 @@ with open("README.md", encoding="utf-8") as f:
5
5
 
6
6
  setup(
7
7
  name="CliRemote",
8
- version="1.6.0",
8
+ version="1.6.1",
9
9
  author="MrAhmadiRad",
10
10
  author_email="mohammadahmadirad69@gmail.com",
11
11
  description="A precise, async-safe, Telegram automation core (Python 3.8+)",
@@ -1,109 +0,0 @@
1
- # remote/client_picker.py
2
- import random
3
- import asyncio
4
- import logging
5
- from typing import Optional, Callable, Iterable
6
-
7
- logger = logging.getLogger(__name__)
8
-
9
- # ---- وابستگی‌های اصلی
10
- # تلاش می‌کنیم client_manager را حتماً داشته باشیم
11
- from . import client_manager
12
-
13
- # get_active() را به‌صورت ایمن تعیین می‌کنیم:
14
- def _resolve_get_active() -> Callable[[], Iterable[str]]:
15
- """
16
- سعی می‌کند منبع معتبر لیست اکانت‌های فعال را پیدا کند:
17
- 1) client_manager.get_active_accounts()
18
- 2) account_manager.get_active_accounts()
19
- 3) account_manager.accounts() (fallback)
20
- 4) client_manager.accounts() (fallback)
21
- و در نهایت اگر هیچ‌کدام نبود، یک فانکشنِ خالی برمی‌گرداند.
22
- """
23
- # 1) client_manager.get_active_accounts
24
- if hasattr(client_manager, "get_active_accounts"):
25
- return client_manager.get_active_accounts
26
-
27
- # 2) account_manager...
28
- try:
29
- from . import account_manager # ممکن است وجود نداشته باشد
30
- if hasattr(account_manager, "get_active_accounts"):
31
- return account_manager.get_active_accounts
32
- if hasattr(account_manager, "accounts"):
33
- return lambda: set(account_manager.accounts())
34
- except Exception:
35
- pass
36
-
37
- # 3) fallback به client_manager.accounts
38
- if hasattr(client_manager, "accounts"):
39
- return lambda: set(client_manager.accounts())
40
-
41
- # 4) آخرین fallback: لیست خالی
42
- return lambda: set()
43
-
44
- _get_active_accounts = _resolve_get_active()
45
-
46
-
47
- async def get_any_client(message=None, max_attempts: int = 3) -> Optional[object]:
48
- """
49
- تلاش برای گرفتن یک کلاینت فعال از بین اکانت‌ها.
50
- - تا `max_attempts` بار با اکانت‌های تصادفی امتحان می‌کند.
51
- - اگر موفق نشد، پیام خطا (در صورت وجود message) ارسال می‌کند،
52
- سپس stop_all_clients() فراخوانی می‌شود و در نهایت None برمی‌گرداند.
53
- """
54
- try:
55
- acc_iter = _get_active_accounts()
56
- acc_list = list(acc_iter) if not isinstance(acc_iter, (list, set, tuple)) else list(acc_iter)
57
- except Exception as e:
58
- logger.error(f"❌ نتوانستم لیست اکانت‌ها را بگیرم: {type(e).__name__} - {e}")
59
- acc_list = []
60
-
61
- if not acc_list:
62
- if message:
63
- try:
64
- await message.reply("⚠️ هیچ اکانت فعالی برای اتصال وجود ندارد.")
65
- except Exception:
66
- pass
67
- logger.warning("⚠️ هیچ اکانت فعالی در دسترس نیست.")
68
- return None
69
-
70
- tried = set()
71
-
72
- for attempt in range(1, max_attempts + 1):
73
- if len(tried) == len(acc_list):
74
- break
75
-
76
- phone = random.choice([p for p in acc_list if p not in tried])
77
- tried.add(phone)
78
- logger.info(f"🔁 تلاش {attempt}/{max_attempts} برای اتصال با اکانت {phone}")
79
-
80
- try:
81
- cli = await client_manager.get_or_start_client(phone)
82
- if cli and getattr(cli, "is_connected", True):
83
- logger.info(f"✅ اتصال موفق با اکانت {phone}")
84
- return cli
85
- else:
86
- logger.warning(f"⚠️ اکانت {phone} وصل نیست یا کلاینت معتبر برنگشته.")
87
- except Exception as e:
88
- logger.error(f"❌ خطا در اتصال {phone}: {type(e).__name__} - {e}")
89
- try:
90
- await asyncio.sleep(1)
91
- except Exception:
92
- pass
93
-
94
- # شکست پس از تلاش‌ها
95
- error_msg = f"❌ هیچ کلاینت فعالی پس از {max_attempts} تلاش یافت نشد. در حال ریست کامل کلاینت‌ها..."
96
- if message:
97
- try:
98
- await message.reply(error_msg)
99
- except Exception:
100
- pass
101
- logger.error(error_msg)
102
-
103
- try:
104
- await client_manager.stop_all_clients()
105
- logger.warning("🔄 تمام کلاینت‌ها ریست شدند (stop_all_clients فراخوانی شد).")
106
- except Exception as e:
107
- logger.error(f"⚠️ خطا در ریست کلاینت‌ها: {type(e).__name__} - {e}")
108
-
109
- return None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes