CliRemote 1.2.2__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 CliRemote might be problematic. Click here for more details.

Files changed (43) hide show
  1. cliremote-1.2.2/CliRemote.egg-info/PKG-INFO +39 -0
  2. cliremote-1.2.2/CliRemote.egg-info/SOURCES.txt +42 -0
  3. cliremote-1.2.2/CliRemote.egg-info/dependency_links.txt +1 -0
  4. cliremote-1.2.2/CliRemote.egg-info/requires.txt +6 -0
  5. cliremote-1.2.2/CliRemote.egg-info/top_level.txt +2 -0
  6. cliremote-1.2.2/LICENSE +21 -0
  7. cliremote-1.2.2/MANIFEST.in +3 -0
  8. cliremote-1.2.2/PKG-INFO +39 -0
  9. cliremote-1.2.2/README.md +3 -0
  10. cliremote-1.2.2/pyproject.toml +47 -0
  11. cliremote-1.2.2/setup.cfg +11 -0
  12. cliremote-1.2.2/setup.py +39 -0
  13. cliremote-1.2.2/src/remote/__init__.py +0 -0
  14. cliremote-1.2.2/src/remote/account_manager.py +161 -0
  15. cliremote-1.2.2/src/remote/account_viewer.py +102 -0
  16. cliremote-1.2.2/src/remote/admin_manager.py +111 -0
  17. cliremote-1.2.2/src/remote/analytics_manager.py +98 -0
  18. cliremote-1.2.2/src/remote/batch_manager.py +34 -0
  19. cliremote-1.2.2/src/remote/block_manager.py +165 -0
  20. cliremote-1.2.2/src/remote/caption_manager.py +25 -0
  21. cliremote-1.2.2/src/remote/cleaner.py +139 -0
  22. cliremote-1.2.2/src/remote/client_manager.py +131 -0
  23. cliremote-1.2.2/src/remote/config.py +63 -0
  24. cliremote-1.2.2/src/remote/device_manager.py +142 -0
  25. cliremote-1.2.2/src/remote/file_sender.py +87 -0
  26. cliremote-1.2.2/src/remote/getcode_controller.py +44 -0
  27. cliremote-1.2.2/src/remote/health.py +31 -0
  28. cliremote-1.2.2/src/remote/help_menu.py +58 -0
  29. cliremote-1.2.2/src/remote/init.py +15 -0
  30. cliremote-1.2.2/src/remote/join_controller.py +34 -0
  31. cliremote-1.2.2/src/remote/joiner.py +77 -0
  32. cliremote-1.2.2/src/remote/leave_controller.py +34 -0
  33. cliremote-1.2.2/src/remote/lefter.py +101 -0
  34. cliremote-1.2.2/src/remote/mention_manager.py +82 -0
  35. cliremote-1.2.2/src/remote/precise_engine.py +19 -0
  36. cliremote-1.2.2/src/remote/profile_info.py +53 -0
  37. cliremote-1.2.2/src/remote/profile_media.py +57 -0
  38. cliremote-1.2.2/src/remote/profile_privacy.py +62 -0
  39. cliremote-1.2.2/src/remote/spammer.py +115 -0
  40. cliremote-1.2.2/src/remote/speed_manager.py +30 -0
  41. cliremote-1.2.2/src/remote/stop_manager.py +30 -0
  42. cliremote-1.2.2/src/remote/text_manager.py +59 -0
  43. cliremote-1.2.2/src/remote/username_manager.py +92 -0
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.4
2
+ Name: CliRemote
3
+ Version: 1.2.2
4
+ Summary: Remote client framework for Telegram automation using Pyrogram
5
+ Home-page: https://github.com/MohammadAhmadi-R/CliRemote
6
+ Author: MrAhmadiRad
7
+ Author-email: MohammadAhmadiRad <mohammadahmadirad69@gmail.com>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/MohammadAhmadi-R/CliRemote
10
+ Keywords: pyrogram,bot,automation,asyncio
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Framework :: AsyncIO
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: pyrogram>=2.0.106
28
+ Requires-Dist: httpx>=0.27.0
29
+ Requires-Dist: pytz>=2023.3
30
+ Provides-Extra: speedups
31
+ Requires-Dist: tgcrypto>=1.2.5; extra == "speedups"
32
+ Dynamic: author
33
+ Dynamic: home-page
34
+ Dynamic: license-file
35
+ Dynamic: requires-python
36
+
37
+ # Remote
38
+ Core Telegram Remote client engine (MrAhmadiRad)
39
+ [GitHub-flavored Markdown](https://github.com/MohammadAhmadi-R/CliRemote)
@@ -0,0 +1,42 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ setup.cfg
6
+ setup.py
7
+ CliRemote.egg-info/PKG-INFO
8
+ CliRemote.egg-info/SOURCES.txt
9
+ CliRemote.egg-info/dependency_links.txt
10
+ CliRemote.egg-info/requires.txt
11
+ CliRemote.egg-info/top_level.txt
12
+ src/remote/__init__.py
13
+ src/remote/account_manager.py
14
+ src/remote/account_viewer.py
15
+ src/remote/admin_manager.py
16
+ src/remote/analytics_manager.py
17
+ src/remote/batch_manager.py
18
+ src/remote/block_manager.py
19
+ src/remote/caption_manager.py
20
+ src/remote/cleaner.py
21
+ src/remote/client_manager.py
22
+ src/remote/config.py
23
+ src/remote/device_manager.py
24
+ src/remote/file_sender.py
25
+ src/remote/getcode_controller.py
26
+ src/remote/health.py
27
+ src/remote/help_menu.py
28
+ src/remote/init.py
29
+ src/remote/join_controller.py
30
+ src/remote/joiner.py
31
+ src/remote/leave_controller.py
32
+ src/remote/lefter.py
33
+ src/remote/mention_manager.py
34
+ src/remote/precise_engine.py
35
+ src/remote/profile_info.py
36
+ src/remote/profile_media.py
37
+ src/remote/profile_privacy.py
38
+ src/remote/spammer.py
39
+ src/remote/speed_manager.py
40
+ src/remote/stop_manager.py
41
+ src/remote/text_manager.py
42
+ src/remote/username_manager.py
@@ -0,0 +1,6 @@
1
+ pyrogram>=2.0.106
2
+ httpx>=0.27.0
3
+ pytz>=2023.3
4
+
5
+ [speedups]
6
+ tgcrypto>=1.2.5
@@ -0,0 +1,2 @@
1
+ dist
2
+ src
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include CliRemote *.py
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.4
2
+ Name: CliRemote
3
+ Version: 1.2.2
4
+ Summary: Remote client framework for Telegram automation using Pyrogram
5
+ Home-page: https://github.com/MohammadAhmadi-R/CliRemote
6
+ Author: MrAhmadiRad
7
+ Author-email: MohammadAhmadiRad <mohammadahmadirad69@gmail.com>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/MohammadAhmadi-R/CliRemote
10
+ Keywords: pyrogram,bot,automation,asyncio
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Framework :: AsyncIO
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: pyrogram>=2.0.106
28
+ Requires-Dist: httpx>=0.27.0
29
+ Requires-Dist: pytz>=2023.3
30
+ Provides-Extra: speedups
31
+ Requires-Dist: tgcrypto>=1.2.5; extra == "speedups"
32
+ Dynamic: author
33
+ Dynamic: home-page
34
+ Dynamic: license-file
35
+ Dynamic: requires-python
36
+
37
+ # Remote
38
+ Core Telegram Remote client engine (MrAhmadiRad)
39
+ [GitHub-flavored Markdown](https://github.com/MohammadAhmadi-R/CliRemote)
@@ -0,0 +1,3 @@
1
+ # Remote
2
+ Core Telegram Remote client engine (MrAhmadiRad)
3
+ [GitHub-flavored Markdown](https://github.com/MohammadAhmadi-R/CliRemote)
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "CliRemote"
7
+ version = "1.2.2"
8
+ description = "Remote client framework for Telegram automation using Pyrogram"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [
12
+ { name = "MohammadAhmadiRad", email = "mohammadahmadirad69@gmail.com" }
13
+ ]
14
+ requires-python = ">=3.13"
15
+
16
+ dependencies = [
17
+ "pyrogram>=2.0.106",
18
+ "httpx>=0.27.0",
19
+ "pytz>=2023.3"
20
+ ]
21
+
22
+ classifiers = [
23
+ "Development Status :: 4 - Beta",
24
+ "Intended Audience :: Developers",
25
+ "License :: OSI Approved :: MIT License",
26
+ "Operating System :: OS Independent",
27
+ "Framework :: AsyncIO",
28
+ "Programming Language :: Python",
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3.8",
31
+ "Programming Language :: Python :: 3.9",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Programming Language :: Python :: 3.13"
36
+ ]
37
+
38
+ keywords = ["pyrogram", "bot", "automation", "asyncio"]
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/MohammadAhmadi-R/CliRemote"
42
+
43
+ [project.optional-dependencies]
44
+ speedups = ["tgcrypto>=1.2.5"]
45
+
46
+ [tool.setuptools]
47
+ packages = { find = { where = ["."] } }
@@ -0,0 +1,11 @@
1
+ [options]
2
+ packages = find:
3
+ include_package_data = True
4
+
5
+ [options.packages.find]
6
+ where = .
7
+
8
+ [egg_info]
9
+ tag_build =
10
+ tag_date = 0
11
+
@@ -0,0 +1,39 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", encoding="utf-8") as f:
4
+ long_description = f.read()
5
+
6
+ setup(
7
+ name="CliRemote",
8
+ version="1.2.3", # ← نسخه جدید
9
+ author="MrAhmadiRad",
10
+ author_email="mohammadahmadirad69@gmail.com",
11
+ description="A precise, async-safe, Telegram automation core (Python 3.8+)",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/MohammadAhmadi-R/CliRemote",
15
+ packages=find_packages(),
16
+ install_requires=[
17
+ "pyrogram>=2.0.106",
18
+ "httpx>=0.27.0",
19
+ "pytz>=2023.3",
20
+ ],
21
+ python_requires=">=3.8",
22
+ classifiers=[
23
+ "Development Status :: 4 - Beta",
24
+ "Intended Audience :: Developers",
25
+ "License :: OSI Approved :: MIT License",
26
+ "Operating System :: OS Independent",
27
+ "Framework :: AsyncIO",
28
+ "Topic :: Communications",
29
+ "Topic :: Communications :: Chat",
30
+ "Programming Language :: Python",
31
+ "Programming Language :: Python :: 3",
32
+ "Programming Language :: Python :: 3.8",
33
+ "Programming Language :: Python :: 3.9",
34
+ "Programming Language :: Python :: 3.10",
35
+ "Programming Language :: Python :: 3.11",
36
+ "Programming Language :: Python :: 3.12",
37
+ "Programming Language :: Python :: 3.13",
38
+ ],
39
+ )
File without changes
@@ -0,0 +1,161 @@
1
+ # antispam_core/account_manager.py
2
+ import os, asyncio, json, logging
3
+ from typing import Optional, Dict, List
4
+ from pyrogram import Client, errors
5
+ from .client_manager import (
6
+ ACCOUNTS_FOLDER,
7
+ get_account_data,
8
+ save_account_data,
9
+ stop_all_clients,
10
+ accounts,
11
+ client_pool,
12
+ client_locks
13
+ )
14
+
15
+ logger = logging.getLogger(__name__)
16
+ login = {}
17
+
18
+ # ==========================
19
+ # افزودن اکانت جدید
20
+ # ==========================
21
+ async def add_account_cmd(message, get_app_info):
22
+ try:
23
+ parts = message.text.split(' ', 1)
24
+ if len(parts) < 2:
25
+ await message.reply('مثال: `add +989123456789`')
26
+ return
27
+
28
+ phone_number = parts[1].strip()
29
+ session_file = os.path.join(ACCOUNTS_FOLDER, f'{phone_number}.session')
30
+
31
+ if os.path.exists(session_file):
32
+ await message.reply('این اکانت وجود دارد!')
33
+ return
34
+
35
+ global login
36
+ api = get_app_info()
37
+ if not api or len(api) < 2:
38
+ await message.reply('مشکل در API!')
39
+ return
40
+
41
+ login['id'] = int(api[1])
42
+ login['hash'] = api[0]
43
+ login['number'] = phone_number
44
+ login['api_data'] = {
45
+ 'api_id': api[1],
46
+ 'api_hash': api[0],
47
+ 'phone_number': phone_number,
48
+ 'session': phone_number,
49
+ '2fa_password': None
50
+ }
51
+
52
+ try:
53
+ login['client'] = Client(name=session_file.replace('.session', ''), api_id=login['id'], api_hash=login['hash'])
54
+ await login['client'].connect()
55
+ login['response'] = await login['client'].send_code(phone_number)
56
+ await message.reply(f'✅ کد تأیید به {phone_number} ارسال شد.\n`code 12345`')
57
+ except errors.BadRequest as e:
58
+ await message.reply(f'Bad request: {str(e)}')
59
+ except errors.FloodWait as e:
60
+ await message.reply(f'Flood wait: {e.value} sec')
61
+ except Exception as e:
62
+ await message.reply(f'Connection error: {str(e)}')
63
+ except Exception as e:
64
+ await message.reply(f'خطا: {str(e)}')
65
+
66
+
67
+ # ==========================
68
+ # تأیید کد ورود
69
+ # ==========================
70
+ async def set_code_cmd(message):
71
+ global login
72
+ parts = message.text.split(' ', 1)
73
+ if len(parts) < 2:
74
+ await message.reply('`code 12345`')
75
+ return
76
+ code = parts[1].strip()
77
+
78
+ try:
79
+ await login['client'].sign_in(login['number'], login['response'].phone_code_hash, code)
80
+ await login['client'].disconnect()
81
+ save_account_data(login['number'], login['api_data'])
82
+ await message.reply(f"✅ اکانت اضافه شد!\n├ شماره: {login['number']}")
83
+ login = {}
84
+ except errors.SessionPasswordNeeded:
85
+ await message.reply('🔒 لطفا رمز را با `pass your_password` بدهید')
86
+ except errors.BadRequest:
87
+ await message.reply('ورود با مشکل مواجه شد')
88
+ except Exception as e:
89
+ await message.reply(f'⚠️ خطا در ورود: {e}')
90
+
91
+
92
+ # ==========================
93
+ # افزودن رمز دومرحله‌ای
94
+ # ==========================
95
+ async def set_2fa_cmd(message):
96
+ global login
97
+ parts = message.text.split(' ', 1)
98
+ if len(parts) < 2:
99
+ await message.reply('`pass my_password`')
100
+ return
101
+ password = parts[1].strip()
102
+ try:
103
+ await login['client'].check_password(password)
104
+ login['api_data']['2fa_password'] = password
105
+ save_account_data(login['number'], login['api_data'])
106
+ await message.reply(f"✅ اکانت با موفقیت اضافه شد!\n├ شماره: {login['number']}")
107
+ await login['client'].disconnect()
108
+ login = {}
109
+ except errors.BadRequest:
110
+ await message.reply('رمز اشتباه است!')
111
+ except Exception as e:
112
+ await message.reply(f'⚠️ خطا در ثبت پسورد: {e}')
113
+
114
+
115
+ # ==========================
116
+ # حذف یک اکانت خاص
117
+ # ==========================
118
+ def remove_client_from_pool(phone_number: str):
119
+ cli = client_pool.get(phone_number)
120
+ if cli:
121
+ try:
122
+ asyncio.create_task(cli.stop())
123
+ except:
124
+ pass
125
+ client_pool.pop(phone_number, None)
126
+ client_locks.pop(phone_number, None)
127
+
128
+
129
+ async def delete_account_cmd(message):
130
+ try:
131
+ phone_number = message.text.split()[1]
132
+ main_path = f'{ACCOUNTS_FOLDER}/{phone_number}.session'
133
+ remove_client_from_pool(phone_number)
134
+ if os.path.isfile(main_path):
135
+ os.unlink(main_path)
136
+ await message.reply('<b>Account deleted successfully.</b>')
137
+ else:
138
+ await message.reply('<b>Account not found in database.</b>')
139
+ except IndexError:
140
+ await message.reply('Please enter phone number')
141
+ except Exception as e:
142
+ await message.reply(f'<b>Error deleting account: {str(e)}</b>')
143
+
144
+
145
+ # ==========================
146
+ # حذف تمام اکانت‌ها
147
+ # ==========================
148
+ async def delete_all_accounts_cmd(message):
149
+ try:
150
+ accountss = accounts()
151
+ count = len(accountss)
152
+ await stop_all_clients()
153
+ for session in accountss:
154
+ main_path = f'{ACCOUNTS_FOLDER}/{session}.session'
155
+ try:
156
+ os.unlink(main_path)
157
+ except Exception:
158
+ pass
159
+ await message.reply(f'<b>{count} accounts deleted.</b>')
160
+ except Exception as e:
161
+ await message.reply(f'<b>Error deleting all accounts: {str(e)}</b>')
@@ -0,0 +1,102 @@
1
+ # antispam_core/account_viewer.py
2
+ import asyncio, logging
3
+ from pyrogram import errors
4
+ from .client_manager import client_pool, get_or_start_client, accounts
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ async def list_accounts_cmd(message):
9
+ """
10
+ نمایش دقیق وضعیت اکانت‌ها (با حفظ اتصال‌ها)
11
+ از client_pool برای بررسی فعال بودن استفاده می‌کند.
12
+ """
13
+ try:
14
+ acc_list = accounts()
15
+ if not acc_list:
16
+ await message.reply('لیست اکانت‌ها:\n(هیچ اکانتی وجود ندارد)')
17
+ return
18
+
19
+ lines = ['📋 <b>لیست اکانت‌ها:</b>']
20
+ success, failed = 0, 0
21
+
22
+ for idx, phone in enumerate(acc_list, start=1):
23
+ try:
24
+ # 🔹 دریافت کلاینت از pool یا ساخت مجدد
25
+ cli = client_pool.get(phone)
26
+ if cli is None or not getattr(cli, 'is_connected', False):
27
+ cli = await get_or_start_client(phone)
28
+
29
+ # تلاش مجدد در صورت قطع بودن
30
+ if cli is None:
31
+ await asyncio.sleep(0.8)
32
+ cli = await get_or_start_client(phone)
33
+
34
+ if cli is None:
35
+ raise Exception("Client could not be started")
36
+
37
+ # 🔁 تا دو بار تلاش برای دریافت اطلاعات
38
+ retry = 0
39
+ me = None
40
+ while retry < 2:
41
+ try:
42
+ me = await cli.get_me()
43
+ if me:
44
+ break
45
+ except errors.FloodWait as e:
46
+ await asyncio.sleep(e.value)
47
+ except Exception:
48
+ retry += 1
49
+ await asyncio.sleep(1)
50
+
51
+ if me:
52
+ success += 1
53
+ full_name = (me.first_name or "") + " " + (me.last_name or "")
54
+ full_name = full_name.strip() or "-"
55
+ uid = me.id
56
+ lines.append(f"\n<b>{idx}. {phone}</b>")
57
+ lines.append(f"Status : ✅ OK")
58
+ lines.append(f"Power : 🟢 ON")
59
+ lines.append(f"Name : {full_name}")
60
+ lines.append(f"UserID : <code>{uid}</code>")
61
+ else:
62
+ failed += 1
63
+ lines.append(f"\n<b>{idx}. {phone}</b>")
64
+ lines.append(f"Status : ❌ ERROR")
65
+ lines.append(f"Power : 🔴 OFF")
66
+ lines.append(f"Name : -")
67
+ lines.append(f"UserID : -")
68
+
69
+ except errors.UserDeactivated:
70
+ failed += 1
71
+ lines.append(f"\n<b>{idx}. {phone}</b>")
72
+ lines.append(f"Status : 🚫 Deactivated")
73
+ lines.append(f"Power : 🔴 OFF")
74
+ lines.append(f"Name : -")
75
+ lines.append(f"UserID : -")
76
+
77
+ except Exception as e:
78
+ failed += 1
79
+ lines.append(f"\n<b>{idx}. {phone}</b>")
80
+ lines.append(f"Status : ⚠️ Error: {str(e)[:40]}")
81
+ lines.append(f"Power : 🔴 OFF")
82
+ lines.append(f"Name : -")
83
+ lines.append(f"UserID : -")
84
+
85
+ await asyncio.sleep(0.5)
86
+
87
+ total = len(acc_list)
88
+ lines.append(f"\n📊 <b>نتیجه:</b>\n✅ سالم: {success}\n❌ خطادار: {failed}\n🔹 مجموع: {total}")
89
+
90
+ text = "\n".join(lines)
91
+
92
+ # ✅ اگر خروجی خیلی طولانی بود، فایل گزارش ارسال شود
93
+ if len(text) > 3900:
94
+ await message.reply_document(
95
+ document=("accounts_report.txt", text.encode('utf-8')),
96
+ caption="📋 گزارش کامل اکانت‌ها"
97
+ )
98
+ else:
99
+ await message.reply(text, disable_web_page_preview=True)
100
+
101
+ except Exception as e:
102
+ await message.reply(f'<b>خطا در نمایش لیست اکانت‌ها:</b>\n{e}')
@@ -0,0 +1,111 @@
1
+ # antispam_core/admin_manager.py
2
+ import json, os, logging
3
+ from pyrogram import filters
4
+ from .config import OWNER_ID
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ ADMINS_FILE = "admins.json"
9
+
10
+
11
+ def load_admins() -> list[int]:
12
+ """
13
+ بارگذاری لیست ادمین‌ها از فایل.
14
+ همیشه OWNER_ID را هم به لیست اضافه می‌کند.
15
+ """
16
+ s = set(OWNER_ID)
17
+ try:
18
+ if os.path.exists(ADMINS_FILE):
19
+ with open(ADMINS_FILE, "r", encoding="utf-8") as f:
20
+ data = json.load(f)
21
+ if isinstance(data, list):
22
+ for v in data:
23
+ try:
24
+ s.add(int(v))
25
+ except:
26
+ pass
27
+ except Exception as e:
28
+ logger.warning(f"Error loading admins: {e}")
29
+ return list(s)
30
+
31
+
32
+ def save_admins():
33
+ """
34
+ ذخیره‌ی ادمین‌ها در فایل.
35
+ """
36
+ try:
37
+ with open(ADMINS_FILE, "w", encoding="utf-8") as f:
38
+ json.dump(list(ADMINS), f, ensure_ascii=False, indent=2)
39
+ except Exception as e:
40
+ logger.error(f"Error saving admins: {e}")
41
+
42
+
43
+ ADMINS = load_admins()
44
+
45
+ # فیلترهای دسترسی برای Pyrogram
46
+ admin_filter = filters.create(
47
+ lambda _, __, m: bool(getattr(m, "from_user", None))
48
+ and int(m.from_user.id) in ADMINS
49
+ )
50
+ owner_filter = filters.create(
51
+ lambda _, __, m: bool(getattr(m, "from_user", None))
52
+ and int(m.from_user.id) in OWNER_ID
53
+ )
54
+
55
+
56
+ # =============================
57
+ # فرمان‌های مدیریتی
58
+ # =============================
59
+
60
+ async def add_admin_cmd(message):
61
+ try:
62
+ parts = message.text.split()
63
+ if len(parts) < 2:
64
+ await message.reply("مثال: addadmin 123456789")
65
+ return
66
+ uid = int(parts[1])
67
+ if uid in OWNER_ID:
68
+ await message.reply("ادمین اصلی از قبل وجود دارد")
69
+ return
70
+ if uid not in ADMINS:
71
+ ADMINS.append(uid)
72
+ save_admins()
73
+ await message.reply(f"ادمین جدید اضافه شد: {uid}")
74
+ else:
75
+ await message.reply("قبلاً ادمین بود")
76
+ except Exception as e:
77
+ logger.error(f"add_admin_cmd error: {e}")
78
+ await message.reply(f"خطا: {e}")
79
+
80
+
81
+ async def del_admin_cmd(message):
82
+ try:
83
+ parts = message.text.split()
84
+ if len(parts) < 2:
85
+ await message.reply("مثال: deladmin 123456789")
86
+ return
87
+ uid = int(parts[1])
88
+ if uid in OWNER_ID:
89
+ await message.reply("❌ امکان حذف ادمین اصلی وجود ندارد")
90
+ return
91
+ if uid in ADMINS:
92
+ ADMINS.remove(uid)
93
+ save_admins()
94
+ await message.reply(f"ادمین حذف شد: {uid}")
95
+ else:
96
+ await message.reply("کاربر ادمین نیست")
97
+ except Exception as e:
98
+ logger.error(f"del_admin_cmd error: {e}")
99
+ await message.reply(f"خطا: {e}")
100
+
101
+
102
+ async def list_admins_cmd(message):
103
+ try:
104
+ if not ADMINS:
105
+ await message.reply("لیست ادمین‌ها خالی است.")
106
+ return
107
+ text = "👑 <b>ADMINS:</b>\n" + "\n".join([str(x) for x in ADMINS])
108
+ await message.reply(text)
109
+ except Exception as e:
110
+ logger.error(f"list_admins_cmd error: {e}")
111
+ await message.reply(f"خطا: {e}")