ravisremotet 1.9.49__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.
- ravisremotet-1.9.49/LICENSE +21 -0
- ravisremotet-1.9.49/MANIFEST.in +3 -0
- ravisremotet-1.9.49/PKG-INFO +32 -0
- ravisremotet-1.9.49/README.md +3 -0
- ravisremotet-1.9.49/handlers/__init__.py +0 -0
- ravisremotet-1.9.49/handlers/core_handler.py +292 -0
- ravisremotet-1.9.49/moudels/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/account/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/account/account_manager.py +295 -0
- ravisremotet-1.9.49/moudels/account/account_viewer.py +135 -0
- ravisremotet-1.9.49/moudels/account/cleaner.py +195 -0
- ravisremotet-1.9.49/moudels/account/client/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/account/client/client_manager.py +462 -0
- ravisremotet-1.9.49/moudels/account/client/flood_guard.py +245 -0
- ravisremotet-1.9.49/moudels/account/client/online_client.py +27 -0
- ravisremotet-1.9.49/moudels/account/client/status_report.py +36 -0
- ravisremotet-1.9.49/moudels/account/client/test.py +42 -0
- ravisremotet-1.9.49/moudels/account/device/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/account/device/device_manager.py +155 -0
- ravisremotet-1.9.49/moudels/account/profile/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/account/profile/profile_info.py +53 -0
- ravisremotet-1.9.49/moudels/account/profile/profile_media.py +89 -0
- ravisremotet-1.9.49/moudels/account/profile/profile_privacy.py +63 -0
- ravisremotet-1.9.49/moudels/account/profile/username_manager.py +107 -0
- ravisremotet-1.9.49/moudels/admin/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/admin/admin_manager.py +187 -0
- ravisremotet-1.9.49/moudels/analytics/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/analytics/analytics_manager.py +199 -0
- ravisremotet-1.9.49/moudels/batch/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/batch/batch_manager.py +108 -0
- ravisremotet-1.9.49/moudels/batch/batch_scheduler.py +107 -0
- ravisremotet-1.9.49/moudels/batch/message_shift.py +217 -0
- ravisremotet-1.9.49/moudels/batch/shift_scheduler.py +155 -0
- ravisremotet-1.9.49/moudels/core/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/core/config.py +107 -0
- ravisremotet-1.9.49/moudels/core/getcode_controller.py +50 -0
- ravisremotet-1.9.49/moudels/core/help_menu.py +83 -0
- ravisremotet-1.9.49/moudels/core/human_behavior.py +38 -0
- ravisremotet-1.9.49/moudels/core/menu_state.py +87 -0
- ravisremotet-1.9.49/moudels/core/precise_engine.py +37 -0
- ravisremotet-1.9.49/moudels/core/response_manager.py +276 -0
- ravisremotet-1.9.49/moudels/core/restart_module.py +133 -0
- ravisremotet-1.9.49/moudels/core/status_panel.py +82 -0
- ravisremotet-1.9.49/moudels/core/typing_indicator.py +159 -0
- ravisremotet-1.9.49/moudels/db/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/db/db_monitor.py +203 -0
- ravisremotet-1.9.49/moudels/db/sqlite_utils.py +215 -0
- ravisremotet-1.9.49/moudels/forward/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/forward/forward_commands.py +258 -0
- ravisremotet-1.9.49/moudels/forward/forwarder.py +553 -0
- ravisremotet-1.9.49/moudels/forward/speed_for_manager.py +50 -0
- ravisremotet-1.9.49/moudels/forward/stop_manager.py +26 -0
- ravisremotet-1.9.49/moudels/group/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/group/join_controller.py +35 -0
- ravisremotet-1.9.49/moudels/group/joiner.py +196 -0
- ravisremotet-1.9.49/moudels/group/leave_controller.py +35 -0
- ravisremotet-1.9.49/moudels/group/lefter.py +155 -0
- ravisremotet-1.9.49/moudels/hybrid/__init__.py +1 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_account_provider.py +20 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_caption.py +80 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_commands.py +165 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_config.py +33 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_diagnostics.py +74 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_forward.py +20 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_forward_sender.py +100 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_manager.py +168 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_scheduler.py +167 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_spam_sender.py +64 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_state.py +78 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_stats.py +117 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_text.py +80 -0
- ravisremotet-1.9.49/moudels/hybrid/hybrid_validator.py +41 -0
- ravisremotet-1.9.49/moudels/menu/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/menu/actions.py +420 -0
- ravisremotet-1.9.49/moudels/menu/backup_legacy_inline/button_actions.py +424 -0
- ravisremotet-1.9.49/moudels/menu/backup_legacy_inline/keyboards.py +259 -0
- ravisremotet-1.9.49/moudels/menu/callback_router.py +759 -0
- ravisremotet-1.9.49/moudels/menu/keyboards.py +303 -0
- ravisremotet-1.9.49/moudels/menu/output_view.py +86 -0
- ravisremotet-1.9.49/moudels/menu/proxy_message.py +109 -0
- ravisremotet-1.9.49/moudels/menu/register.py +49 -0
- ravisremotet-1.9.49/moudels/menu/schema.py +27 -0
- ravisremotet-1.9.49/moudels/menu/state_catchers.py +536 -0
- ravisremotet-1.9.49/moudels/menu/state_manager.py +41 -0
- ravisremotet-1.9.49/moudels/spammer/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/spammer/backup/spammer.py +305 -0
- ravisremotet-1.9.49/moudels/spammer/backup/stop_manager.py +33 -0
- ravisremotet-1.9.49/moudels/spammer/spam_caption.py +77 -0
- ravisremotet-1.9.49/moudels/spammer/spam_commands.py +120 -0
- ravisremotet-1.9.49/moudels/spammer/spam_text.py +76 -0
- ravisremotet-1.9.49/moudels/spammer/spammer.py +588 -0
- ravisremotet-1.9.49/moudels/spammer/speed_manager.py +26 -0
- ravisremotet-1.9.49/moudels/spammer/stop_manager.py +22 -0
- ravisremotet-1.9.49/moudels/text/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/text/caption_manager.py +56 -0
- ravisremotet-1.9.49/moudels/text/finaly_text.py +240 -0
- ravisremotet-1.9.49/moudels/text/mention_manager.py +286 -0
- ravisremotet-1.9.49/moudels/text/text_manager.py +59 -0
- ravisremotet-1.9.49/moudels/utils/__init__.py +0 -0
- ravisremotet-1.9.49/moudels/utils/block_manager.py +156 -0
- ravisremotet-1.9.49/moudels/utils/file_sender.py +137 -0
- ravisremotet-1.9.49/moudels/utils/health.py +31 -0
- ravisremotet-1.9.49/moudels/utils/humanize.py +75 -0
- ravisremotet-1.9.49/pyproject.toml +37 -0
- ravisremotet-1.9.49/ravisremotet.egg-info/PKG-INFO +32 -0
- ravisremotet-1.9.49/ravisremotet.egg-info/SOURCES.txt +209 -0
- ravisremotet-1.9.49/ravisremotet.egg-info/dependency_links.txt +1 -0
- ravisremotet-1.9.49/ravisremotet.egg-info/requires.txt +3 -0
- ravisremotet-1.9.49/ravisremotet.egg-info/top_level.txt +4 -0
- ravisremotet-1.9.49/setup.cfg +11 -0
- ravisremotet-1.9.49/setup.py +39 -0
|
@@ -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,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ravisremotet
|
|
3
|
+
Version: 1.9.49
|
|
4
|
+
Summary: Remote client framework for Telegram automation using Pyrogram
|
|
5
|
+
Home-page: https://github.com/MohammadAhmadi-R/ravisremote
|
|
6
|
+
Author: MrAhmadiRad
|
|
7
|
+
Author-email: MohammadAhmadiRad <mohammadahmadirad69@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/MohammadAhmadi-R/ravisremote
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Framework :: AsyncIO
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: pyrogram>=2.0.106
|
|
23
|
+
Requires-Dist: httpx>=0.27.0
|
|
24
|
+
Requires-Dist: pytz>=2023.3
|
|
25
|
+
Dynamic: author
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
Dynamic: requires-python
|
|
29
|
+
|
|
30
|
+
# Remote
|
|
31
|
+
Core Telegram Remote client engine (MrAhmadiRad)
|
|
32
|
+
[GitHub-flavored Markdown](https://github.com/MohammadAhmadi-R/CliRemote)
|
|
File without changes
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
from pyrogram import filters , errors
|
|
2
|
+
from pyrogram.types import Message
|
|
3
|
+
from ..moudels.admin.admin_manager import (admin_filter , owner_filter)
|
|
4
|
+
from ..moudels.account import (account_manager,account_viewer,cleaner)
|
|
5
|
+
from ..moudels.account.device import (device_manager)
|
|
6
|
+
from ..moudels.account.profile import (profile_info,profile_media,profile_privacy,username_manager)
|
|
7
|
+
from ..moudels.admin import (admin_manager)
|
|
8
|
+
from ..moudels.analytics import (analytics_manager)
|
|
9
|
+
from ..moudels.batch import (batch_manager)
|
|
10
|
+
from ..moudels.core import (config,restart_module,getcode_controller,help_menu,status_panel)
|
|
11
|
+
from ..moudels.db import (db_monitor)
|
|
12
|
+
from ..moudels.group import (join_controller,leave_controller)
|
|
13
|
+
from ..moudels.text import (caption_manager,mention_manager,text_manager)
|
|
14
|
+
from ..moudels.utils import (block_manager,file_sender)
|
|
15
|
+
from ..moudels.menu.register import register_menu
|
|
16
|
+
from ..moudels.forward import (speed_for_manager,forward_commands)
|
|
17
|
+
import logging
|
|
18
|
+
logging.basicConfig(
|
|
19
|
+
level=logging.INFO,
|
|
20
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
21
|
+
)
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
logging.getLogger("asyncio").setLevel(logging.ERROR)
|
|
24
|
+
logging.getLogger("pyrogram").setLevel(logging.WARNING)
|
|
25
|
+
|
|
26
|
+
def register_commands(app):
|
|
27
|
+
|
|
28
|
+
# منوی /start + دکمههای اینلاین (کاملاً جدا از دستورات متنی زیر)
|
|
29
|
+
register_menu(app)
|
|
30
|
+
|
|
31
|
+
# این دستور جزئی از فلوی دکمهای فوروارد است و حتی وقتی بقیه دستورات
|
|
32
|
+
# متنی خاموشاند باید فعال بماند: روی پیام فورواردشده ریپلای میشود.
|
|
33
|
+
@app.on_message(filters.command("for", prefixes=["/", ""]) & admin_filter)
|
|
34
|
+
async def register_forwarded_message(client, message):
|
|
35
|
+
await forward_commands.register_replied_forward(client, message)
|
|
36
|
+
|
|
37
|
+
# 🔒 دستورات متنی فعلاً غیرفعالن — همهچی فقط از طریق دکمههای شیشهای
|
|
38
|
+
# (اینلاین) انجام میشه. برای فعالسازی دوباره فقط کافیه توی
|
|
39
|
+
# moudels/core/config.py مقدار ENABLE_TEXT_COMMANDS رو True کنی.
|
|
40
|
+
if not config.ENABLE_TEXT_COMMANDS:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
# -------------------- Commands --------------------
|
|
44
|
+
|
|
45
|
+
#ACCOUNT
|
|
46
|
+
@app.on_message(filters.command("add", prefixes=["/", ""]) & admin_filter)
|
|
47
|
+
async def add_account(client, message):
|
|
48
|
+
await account_manager.add_account_cmd(message, account_manager.get_app_info)
|
|
49
|
+
|
|
50
|
+
@app.on_message(filters.command("code", prefixes=["/", ""]) & admin_filter)
|
|
51
|
+
async def set_code(client, message):
|
|
52
|
+
await account_manager.set_code_cmd(message)
|
|
53
|
+
|
|
54
|
+
@app.on_message(filters.command("pass", prefixes=["/", ""]) & admin_filter)
|
|
55
|
+
async def set_2fa(client, message):
|
|
56
|
+
await account_manager.set_2fa_cmd(message)
|
|
57
|
+
|
|
58
|
+
@app.on_message(filters.command("del", prefixes=["/", ""]) & admin_filter)
|
|
59
|
+
async def delete_account(client, message):
|
|
60
|
+
await account_manager.delete_account_cmd(message)
|
|
61
|
+
|
|
62
|
+
@app.on_message(filters.command("delall", prefixes=["/", ""]) & admin_filter)
|
|
63
|
+
async def delete_all_accounts(client, message):
|
|
64
|
+
await account_manager.delete_all_accounts_cmd(message)
|
|
65
|
+
|
|
66
|
+
@app.on_message(filters.command("listacc", prefixes=["/", ""]) & admin_filter)
|
|
67
|
+
async def list_accounts(client, message):
|
|
68
|
+
await account_viewer.list_accounts_cmd(message)
|
|
69
|
+
|
|
70
|
+
@app.on_message(filters.command("givedatasessions", prefixes=["/", ""]) & owner_filter)
|
|
71
|
+
async def give_data_sessions_handler(client, message):
|
|
72
|
+
await file_sender.give_data_sessions_cmd(app, message)
|
|
73
|
+
|
|
74
|
+
@app.on_message(filters.command("backupzip", prefixes=["/", ""]) & owner_filter)
|
|
75
|
+
async def give_backup_zip_handler(client, message):
|
|
76
|
+
await file_sender.give_full_backup_zip_cmd(app, message)
|
|
77
|
+
|
|
78
|
+
@app.on_message(filters.command("sysstatus", prefixes=["/", ""]) & admin_filter)
|
|
79
|
+
async def sys_status_handler(client, message):
|
|
80
|
+
await status_panel.show_status(app, message)
|
|
81
|
+
|
|
82
|
+
@app.on_message(filters.command("delallpvgpchenl", "") & owner_filter)
|
|
83
|
+
async def del_all_pv_gp_ch_en(client, message):
|
|
84
|
+
await cleaner.del_all_pv_gp_ch_en_cmd(message)
|
|
85
|
+
|
|
86
|
+
@app.on_message(filters.command("givesessions", "") & owner_filter)
|
|
87
|
+
async def give_sessions_handler(client, message):
|
|
88
|
+
await file_sender.give_sessions_cmd(app, message)
|
|
89
|
+
|
|
90
|
+
@app.on_message(filters.command("delalldevices", "") & admin_filter)
|
|
91
|
+
async def kill_other_sessions(client, message):
|
|
92
|
+
await device_manager.terminate_all_other_sessions_cmd(message)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
#TEXT
|
|
97
|
+
@app.on_message(filters.command('text', prefixes=["/", ""]) & admin_filter)
|
|
98
|
+
async def save_text(client, message):
|
|
99
|
+
await text_manager.save_text_cmd(message)
|
|
100
|
+
|
|
101
|
+
@app.on_message(filters.command('ctext', prefixes=["/", ""]) & admin_filter)
|
|
102
|
+
async def clear_texts(client, message):
|
|
103
|
+
await text_manager.clear_texts_cmd(message)
|
|
104
|
+
|
|
105
|
+
@app.on_message(filters.command('shtext', prefixes=["/", ""]) & admin_filter)
|
|
106
|
+
async def show_text(client, message):
|
|
107
|
+
await text_manager.show_texts_cmd(message)
|
|
108
|
+
|
|
109
|
+
@app.on_message(filters.command('shcap', prefixes=["/", ""]) & admin_filter)
|
|
110
|
+
async def show_caption(client, message):
|
|
111
|
+
await caption_manager.show_caption_cmd(message, config.spam_config)
|
|
112
|
+
|
|
113
|
+
@app.on_message(filters.command('cap', prefixes=["/", ""]) & admin_filter)
|
|
114
|
+
async def add_caption(client, message):
|
|
115
|
+
await caption_manager.add_caption_cmd(message, config.spam_config)
|
|
116
|
+
|
|
117
|
+
@app.on_message(filters.command('ccap', prefixes=["/", ""]) & admin_filter)
|
|
118
|
+
async def clear_caption(client, message):
|
|
119
|
+
await caption_manager.clear_caption_cmd(message, config.spam_config)
|
|
120
|
+
|
|
121
|
+
@app.on_message(admin_filter & filters.command("textmention", prefixes=["/", ""]))
|
|
122
|
+
async def _setmention(client, m: Message):
|
|
123
|
+
txt = m.text.split(None, 1)[1] if (m.text and len(m.command) > 1) else ""
|
|
124
|
+
await m.reply(await mention_manager.set_mention_text(txt))
|
|
125
|
+
|
|
126
|
+
@app.on_message(admin_filter & filters.command("mention_user", prefixes=["/", ""]))
|
|
127
|
+
async def _mention_user(client, m: Message):
|
|
128
|
+
user = m.command[1]
|
|
129
|
+
await m.reply(await mention_manager.set_mention_user(user))
|
|
130
|
+
|
|
131
|
+
@app.on_message(admin_filter & filters.command("mention_toggle", prefixes=["/", ""]))
|
|
132
|
+
async def _mention_toggle(client, m: Message):
|
|
133
|
+
if len(m.command) < 2:
|
|
134
|
+
return await m.reply("Usage: /mention_toggle <on|off>")
|
|
135
|
+
enable = (m.command[1].lower() == "on")
|
|
136
|
+
await m.reply(await mention_manager.toggle_mention(enable))
|
|
137
|
+
|
|
138
|
+
@app.on_message(admin_filter & filters.command("mention_group_toggle", prefixes=["/", ""]))
|
|
139
|
+
async def _mention_group_toggle(client, m: Message):
|
|
140
|
+
if len(m.command) < 2:
|
|
141
|
+
return await m.reply("Usage: /mention_group_toggle <on|off>")
|
|
142
|
+
enable = (m.command[1].lower() == "on")
|
|
143
|
+
await m.reply(await mention_manager.toggle_group_mention(enable))
|
|
144
|
+
|
|
145
|
+
@app.on_message(admin_filter & filters.command("mention_gps", prefixes=["/", ""]))
|
|
146
|
+
async def _mention_gps(client, m: Message):
|
|
147
|
+
if not (m.text and len(m.command) > 1):
|
|
148
|
+
return await m.reply("Usage: /mention_gps <id1> <id2> ...")
|
|
149
|
+
tokens = m.command[1:]
|
|
150
|
+
ids = await mention_manager._resolve_many_tokens_to_ids(client, tokens)
|
|
151
|
+
if not ids:
|
|
152
|
+
return await m.reply("❌ هیچ شناسهٔ معتبری تشخیص داده نشد.")
|
|
153
|
+
msg = await mention_manager.add_groups_by_ids(*ids)
|
|
154
|
+
await m.reply(msg)
|
|
155
|
+
|
|
156
|
+
await m.reply(msg)
|
|
157
|
+
|
|
158
|
+
@app.on_message(admin_filter & filters.command("mention_del", prefixes=["/", ""]))
|
|
159
|
+
async def _mention_del(client, m: Message):
|
|
160
|
+
if not (m.text and len(m.command) > 1):
|
|
161
|
+
return await m.reply("Usage: /mention_del <id1> <id2> ...")
|
|
162
|
+
tokens = m.command[1:]
|
|
163
|
+
ids = await mention_manager._resolve_many_tokens_to_ids(client, tokens)
|
|
164
|
+
if not ids:
|
|
165
|
+
return await m.reply("❌ هیچ شناسهٔ معتبری برای حذف تشخیص داده نشد.")
|
|
166
|
+
msg = await mention_manager.remove_groups_by_ids(*ids)
|
|
167
|
+
await m.reply(msg)
|
|
168
|
+
|
|
169
|
+
@app.on_message(admin_filter & filters.command("mention_clear", prefixes=["/", ""]))
|
|
170
|
+
async def _mention_clear(client, m: Message):
|
|
171
|
+
await m.reply(await mention_manager.clear_groups())
|
|
172
|
+
|
|
173
|
+
@app.on_message(admin_filter & filters.command("mention_status", prefixes=["/", ""]))
|
|
174
|
+
async def _mention_status(client, m: Message):
|
|
175
|
+
await m.reply(await mention_manager.mention_status())
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
#MORE
|
|
179
|
+
@app.on_message(filters.command('gcode', '') & admin_filter)
|
|
180
|
+
async def get_code_command(client, message):
|
|
181
|
+
await getcode_controller.handle_getcode_cmd(message)
|
|
182
|
+
|
|
183
|
+
@app.on_message(filters.command("restart", prefixes=["/", ""]) & admin_filter)
|
|
184
|
+
async def restart_cmd(client, message):
|
|
185
|
+
restart_module.clear_logs()
|
|
186
|
+
await message.reply("عملیات ریست انجام شد؛ لاگها و آمار اسپمر و فوروارد پاک شدند.")
|
|
187
|
+
|
|
188
|
+
#JOIN & LEFT
|
|
189
|
+
@app.on_message(filters.command("join", "") & admin_filter)
|
|
190
|
+
async def join_command(client, message):
|
|
191
|
+
await join_controller.handle_join_cmd(message)
|
|
192
|
+
|
|
193
|
+
@app.on_message(filters.command("leave", "") & admin_filter)
|
|
194
|
+
async def leave_command(client, message):
|
|
195
|
+
await leave_controller.handle_leave_cmd(message)
|
|
196
|
+
|
|
197
|
+
@app.on_message(filters.command("setshift", "") & admin_filter)
|
|
198
|
+
async def set_shift_command(client, message):
|
|
199
|
+
await batch_manager._set_shift_config_cmd(client, message)
|
|
200
|
+
|
|
201
|
+
#ADMIN
|
|
202
|
+
@app.on_message(filters.command("addadmin", "") & owner_filter)
|
|
203
|
+
async def add_admin(client, message):
|
|
204
|
+
await admin_manager.add_admin_cmd(message)
|
|
205
|
+
|
|
206
|
+
@app.on_message(filters.command("deladmin", "") & owner_filter)
|
|
207
|
+
async def del_admin(client, message):
|
|
208
|
+
await admin_manager.del_admin_cmd(message)
|
|
209
|
+
|
|
210
|
+
@app.on_message(filters.command("admins", "") & owner_filter)
|
|
211
|
+
async def list_admins(client, message):
|
|
212
|
+
await admin_manager.list_admins_cmd(message)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
#PROFILE
|
|
216
|
+
@app.on_message(filters.command("profilesettings", "") & admin_filter)
|
|
217
|
+
async def profilesettings_cmd(client, message):
|
|
218
|
+
await profile_privacy.profile_settings_cmd(message)
|
|
219
|
+
|
|
220
|
+
@app.on_message(filters.command("setPic", "") & admin_filter)
|
|
221
|
+
async def set_profile_photo_cmd(client, message):
|
|
222
|
+
await profile_media.change_profile_photo(app, message)
|
|
223
|
+
|
|
224
|
+
@app.on_message(filters.command("delallprofile", "") & admin_filter)
|
|
225
|
+
async def delete_all_photos_cmd(client, message):
|
|
226
|
+
await profile_media.delete_all_profile_photos(message)
|
|
227
|
+
|
|
228
|
+
@app.on_message(filters.command("dellastpic", "") & admin_filter)
|
|
229
|
+
async def delete_last_photo_cmd(client, message):
|
|
230
|
+
await profile_media.delete_last_profile_photo(message)
|
|
231
|
+
|
|
232
|
+
@app.on_message(filters.command("name", "") & admin_filter)
|
|
233
|
+
async def change_name_cmd(client, message):
|
|
234
|
+
await profile_info.change_name_cmd(message)
|
|
235
|
+
|
|
236
|
+
@app.on_message(filters.command("bio", "") & admin_filter)
|
|
237
|
+
async def change_bio_cmd(client, message):
|
|
238
|
+
await profile_info.change_bio_cmd(message)
|
|
239
|
+
|
|
240
|
+
@app.on_message(filters.command("username", "") & admin_filter)
|
|
241
|
+
async def set_username_cmd(client, message):
|
|
242
|
+
await username_manager.set_usernames_for_all(message)
|
|
243
|
+
|
|
244
|
+
@app.on_message(filters.command("remusername", "") & admin_filter)
|
|
245
|
+
async def rem_username_cmd(client, message):
|
|
246
|
+
await username_manager.remove_usernames_for_all(message)
|
|
247
|
+
|
|
248
|
+
@app.on_message(filters.command("block", "") & admin_filter)
|
|
249
|
+
async def block_user_all_accounts(client, message):
|
|
250
|
+
await block_manager.block_user_all_cmd(message)
|
|
251
|
+
|
|
252
|
+
@app.on_message(filters.command("unblock", "") & admin_filter)
|
|
253
|
+
async def unblock_user_all_accounts(client, message):
|
|
254
|
+
await block_manager.unblock_user_all_cmd(message)
|
|
255
|
+
|
|
256
|
+
#DATABASE
|
|
257
|
+
@app.on_message(filters.command("dbstatus", "") & owner_filter)
|
|
258
|
+
async def cmd_db_status(client, message):
|
|
259
|
+
await db_monitor.db_status_cmd(message)
|
|
260
|
+
|
|
261
|
+
@app.on_message(filters.command("dbrepair", "") & owner_filter)
|
|
262
|
+
async def cmd_db_repair(client, message):
|
|
263
|
+
await db_monitor.db_repair_cmd(message)
|
|
264
|
+
|
|
265
|
+
@app.on_message(filters.command("set", "") & admin_filter)
|
|
266
|
+
async def _set_handler(client, message):
|
|
267
|
+
await batch_manager._set_batch_spam_size_cmd(client, message)
|
|
268
|
+
|
|
269
|
+
@app.on_message(filters.command("stats", "") & admin_filter)
|
|
270
|
+
async def show_stats(client, message):
|
|
271
|
+
await analytics_manager.analytics.show_stats_cmd(message)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@app.on_message(filters.command("speed_for", "") & admin_filter)
|
|
275
|
+
async def set_speed_for(client, message):
|
|
276
|
+
await speed_for_manager.set_speed_forward_cmd(message)
|
|
277
|
+
|
|
278
|
+
@app.on_message(filters.command("forward", "") & admin_filter)
|
|
279
|
+
async def start_forward(client, message):
|
|
280
|
+
await forward_commands.start_forward_cmd(message)
|
|
281
|
+
|
|
282
|
+
@app.on_message(filters.command("stop_for", "") & admin_filter)
|
|
283
|
+
async def stop_forward(client, message):
|
|
284
|
+
await forward_commands.stop_forward_cmd(message)
|
|
285
|
+
|
|
286
|
+
@app.on_message(filters.command("set_for", "") & admin_filter)
|
|
287
|
+
async def stop_forward(client, message):
|
|
288
|
+
await forward_commands.change_batch_forward(message)
|
|
289
|
+
|
|
290
|
+
@app.on_message(filters.command("ham_for", "") & admin_filter)
|
|
291
|
+
async def stop_forward(client, message):
|
|
292
|
+
await forward_commands.change_hamzamani_forward(message)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
from pyrogram import Client, errors
|
|
5
|
+
import random
|
|
6
|
+
from typing import List
|
|
7
|
+
from .client.client_manager import *
|
|
8
|
+
|
|
9
|
+
# ============================================================
|
|
10
|
+
# ⚙️ تنظیمات لاگ
|
|
11
|
+
# ============================================================
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
logger.setLevel(logging.INFO)
|
|
14
|
+
|
|
15
|
+
# ============================================================
|
|
16
|
+
# 📦 انتخاب API ID/HASH
|
|
17
|
+
# ============================================================
|
|
18
|
+
def get_app_info() -> List[str]:
|
|
19
|
+
"""برمیگرداند: [api_hash, api_id]"""
|
|
20
|
+
try:
|
|
21
|
+
apis = {
|
|
22
|
+
1: ['debac98afc137d3a82df5454f345bf02', 23523087],
|
|
23
|
+
2: ['b86bbf4b700b4e922fff2c05b3b8985f', 17221354],
|
|
24
|
+
3: ['2345124333c84e4f72441606a08e882c', 21831682],
|
|
25
|
+
4: ['1ebc2808ef58a95bc796590151c3e0d5', 14742007],
|
|
26
|
+
5: ['b8eff20a7e8adcdaa3daa3bc789a5b41', 12176206]
|
|
27
|
+
}
|
|
28
|
+
return apis[random.randint(1, 5)]
|
|
29
|
+
except Exception as e:
|
|
30
|
+
logger.error(f'Error reading app info: {e}')
|
|
31
|
+
return []
|
|
32
|
+
|
|
33
|
+
# ============================================================
|
|
34
|
+
# 🧠 وضعیت موقت ورود (Login State)
|
|
35
|
+
# ============================================================
|
|
36
|
+
login_state = {}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _login_value(raw_text: str, legacy_command: str) -> str:
|
|
40
|
+
"""ورودی خام منو را میپذیرد و با قالب دستوری قدیمی هم سازگار میماند."""
|
|
41
|
+
value = (raw_text or '').strip()
|
|
42
|
+
head, separator, payload = value.partition(' ')
|
|
43
|
+
if separator and head.lower().lstrip('/') == legacy_command:
|
|
44
|
+
return payload.strip()
|
|
45
|
+
return value
|
|
46
|
+
|
|
47
|
+
# ============================================================
|
|
48
|
+
# 🔧 نرمالسازی شماره تلفن
|
|
49
|
+
# ============================================================
|
|
50
|
+
def normalize_phone_number(raw: str) -> str:
|
|
51
|
+
"""
|
|
52
|
+
نرمالسازی شماره تلفن بینالمللی:
|
|
53
|
+
- حذف فاصلهها، خط تیره و علائم اضافی
|
|
54
|
+
- بررسی شروع با '+'
|
|
55
|
+
- هیچ پیشفرض کشوری اضافه نمیکند (مثل +98)
|
|
56
|
+
"""
|
|
57
|
+
if not raw:
|
|
58
|
+
return ""
|
|
59
|
+
s = str(raw).strip()
|
|
60
|
+
# حذف فاصله، خط تیره، پرانتز و سایر علائم
|
|
61
|
+
s = s.replace(" ", "").replace("-", "").replace("(", "").replace(")", "")
|
|
62
|
+
# حذف کاراکترهای غیرعددی غیر از '+'
|
|
63
|
+
s = "".join(ch for ch in s if ch.isdigit() or ch == "+")
|
|
64
|
+
# اطمینان از اینکه فقط یک '+' در ابتدای شماره است
|
|
65
|
+
if s.count("+") > 1:
|
|
66
|
+
s = "+" + s.replace("+", "")
|
|
67
|
+
if not s.startswith("+"):
|
|
68
|
+
# شمارهای که بدون پیششماره کشور داده شده
|
|
69
|
+
# همونطور نگه میداریم ولی هشدار لاگ میفرستیم
|
|
70
|
+
logger.warning(f"normalize_phone_number: شماره بدون پیششماره کشور: {s}")
|
|
71
|
+
return s
|
|
72
|
+
|
|
73
|
+
# ============================================================
|
|
74
|
+
# 🔹 افزودن اکانت جدید
|
|
75
|
+
# ============================================================
|
|
76
|
+
async def add_account_cmd(message, get_app_info_fn):
|
|
77
|
+
"""اضافهکردن اکانت جدید با شماره و ثبت API info"""
|
|
78
|
+
try:
|
|
79
|
+
parts = message.text.split(' ', 1)
|
|
80
|
+
if len(parts) < 2:
|
|
81
|
+
await message.reply('📱 مثال:\n`add +989123456789`')
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
phone_number = normalize_phone_number(parts[1])
|
|
85
|
+
if not phone_number:
|
|
86
|
+
await message.reply("⚠️ شماره تلفن معتبر نیست.")
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
session_file = os.path.join(ACCOUNTS_FOLDER, f'{phone_number}.session')
|
|
90
|
+
if os.path.exists(session_file):
|
|
91
|
+
await message.reply('⚠️ این اکانت قبلاً ثبت شده است.')
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
api_info = get_app_info_fn()
|
|
95
|
+
if not api_info or len(api_info) < 2:
|
|
96
|
+
await message.reply('❌ خطا در دریافت API ID / HASH')
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
api_hash, api_id = api_info
|
|
100
|
+
device_model, system_version, app_version = get_or_assign_device_for_account(phone_number)
|
|
101
|
+
|
|
102
|
+
login_state.update({
|
|
103
|
+
'phone': phone_number,
|
|
104
|
+
'api_id': api_id,
|
|
105
|
+
'api_hash': api_hash,
|
|
106
|
+
'session': phone_number,
|
|
107
|
+
'device_model': device_model,
|
|
108
|
+
'system_version': system_version,
|
|
109
|
+
'app_version': app_version,
|
|
110
|
+
'2fa_password': None
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
client = Client(
|
|
114
|
+
name=session_file.replace('.session', ''),
|
|
115
|
+
api_id=api_id,
|
|
116
|
+
api_hash=api_hash,
|
|
117
|
+
device_model=device_model,
|
|
118
|
+
system_version=system_version,
|
|
119
|
+
app_version=app_version
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
await client.connect()
|
|
123
|
+
sent = await client.send_code(phone_number)
|
|
124
|
+
login_state['client'] = client
|
|
125
|
+
login_state['sent_code'] = sent
|
|
126
|
+
|
|
127
|
+
await message.reply(
|
|
128
|
+
f"✅ کد تأیید به شماره **{phone_number}** ارسال شد.\n"
|
|
129
|
+
f"📥 فقط خود کد را بفرست؛ برای مثال: `12345`"
|
|
130
|
+
)
|
|
131
|
+
logger.info("%s: code sent successfully (%s)", phone_number, device_model)
|
|
132
|
+
|
|
133
|
+
except errors.FloodWait as e:
|
|
134
|
+
await message.reply(f'⏳ FloodWait: {e.value} ثانیه')
|
|
135
|
+
except errors.BadRequest as e:
|
|
136
|
+
await message.reply(f'⚠️ Bad Request: {str(e)}')
|
|
137
|
+
except Exception as e:
|
|
138
|
+
logger.error("add_account_cmd error: %s", e)
|
|
139
|
+
await message.reply(f'❌ خطا: {str(e)}')
|
|
140
|
+
|
|
141
|
+
# ============================================================
|
|
142
|
+
# 🔹 تأیید کد ارسالشده
|
|
143
|
+
# ============================================================
|
|
144
|
+
async def set_code_cmd(message):
|
|
145
|
+
"""تأیید کد ارسالشده برای ورود اولیه"""
|
|
146
|
+
try:
|
|
147
|
+
if not login_state or 'client' not in login_state:
|
|
148
|
+
await message.reply("⚠️ ابتدا از بخش افزودن حساب، شماره را ثبت کن.")
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
code = _login_value(message.text, 'code')
|
|
152
|
+
if not code:
|
|
153
|
+
await message.reply('📨 فقط خود کد را بفرست؛ مثال: `12345`')
|
|
154
|
+
return
|
|
155
|
+
if not code.isdigit():
|
|
156
|
+
await message.reply('📨 کد باید فقط شامل عدد باشد؛ مثال: `12345`')
|
|
157
|
+
return
|
|
158
|
+
phone_number = login_state['phone']
|
|
159
|
+
client = login_state['client']
|
|
160
|
+
sent_code = login_state['sent_code']
|
|
161
|
+
|
|
162
|
+
await client.sign_in(phone_number, sent_code.phone_code_hash, code)
|
|
163
|
+
await client.disconnect()
|
|
164
|
+
|
|
165
|
+
data = {
|
|
166
|
+
"api_id": login_state['api_id'],
|
|
167
|
+
"api_hash": login_state['api_hash'],
|
|
168
|
+
"session": phone_number,
|
|
169
|
+
"2fa_password": None,
|
|
170
|
+
"device_model": login_state['device_model'],
|
|
171
|
+
"system_version": login_state['system_version'],
|
|
172
|
+
"app_version": login_state['app_version'],
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
save_account_data(phone_number, data)
|
|
176
|
+
await message.reply(f"✅ اکانت با موفقیت اضافه شد.\n📱 شماره: {phone_number}")
|
|
177
|
+
logger.info("%s: account added successfully", phone_number)
|
|
178
|
+
login_state.clear()
|
|
179
|
+
|
|
180
|
+
except errors.SessionPasswordNeeded:
|
|
181
|
+
await message.reply('🔐 فقط رمز دومرحلهای را در همین مرحله بفرست.')
|
|
182
|
+
except errors.BadRequest as e:
|
|
183
|
+
await message.reply(f'⚠️ ورود با مشکل مواجه شد: {str(e)}')
|
|
184
|
+
except Exception as e:
|
|
185
|
+
logger.error("set_code_cmd error: %s", e)
|
|
186
|
+
await message.reply(f'❌ خطا: {str(e)}')
|
|
187
|
+
|
|
188
|
+
# ============================================================
|
|
189
|
+
# 🔹 رمز دو مرحلهای
|
|
190
|
+
# ============================================================
|
|
191
|
+
async def set_2fa_cmd(message):
|
|
192
|
+
"""ورود رمز دومرحلهای (در صورت نیاز)"""
|
|
193
|
+
try:
|
|
194
|
+
if not login_state or 'client' not in login_state:
|
|
195
|
+
await message.reply("⚠️ ابتدا از بخش افزودن حساب، شماره و کد را ثبت کن.")
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
password = _login_value(message.text, 'pass')
|
|
199
|
+
if not password:
|
|
200
|
+
await message.reply('🔑 فقط خود رمز دومرحلهای را بفرست.')
|
|
201
|
+
return
|
|
202
|
+
client = login_state['client']
|
|
203
|
+
phone_number = login_state['phone']
|
|
204
|
+
|
|
205
|
+
await client.check_password(password)
|
|
206
|
+
await client.disconnect()
|
|
207
|
+
|
|
208
|
+
data = {
|
|
209
|
+
"api_id": login_state['api_id'],
|
|
210
|
+
"api_hash": login_state['api_hash'],
|
|
211
|
+
"session": phone_number,
|
|
212
|
+
"2fa_password": password,
|
|
213
|
+
"device_model": login_state['device_model'],
|
|
214
|
+
"system_version": login_state['system_version'],
|
|
215
|
+
"app_version": login_state['app_version'],
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
save_account_data(phone_number, data)
|
|
219
|
+
await message.reply(f"✅ اکانت با موفقیت ثبت شد!\n📱 شماره: {phone_number}")
|
|
220
|
+
logger.info("%s: 2FA added and saved.", phone_number)
|
|
221
|
+
login_state.clear()
|
|
222
|
+
|
|
223
|
+
except errors.BadRequest:
|
|
224
|
+
await message.reply('❌ رمز اشتباه است!')
|
|
225
|
+
except Exception as e:
|
|
226
|
+
logger.error("set_2fa_cmd error: %s", e)
|
|
227
|
+
await message.reply(f'⚠️ خطا: {e}')
|
|
228
|
+
|
|
229
|
+
# ============================================================
|
|
230
|
+
# 🔹 حذف اکانت خاص
|
|
231
|
+
# ============================================================
|
|
232
|
+
async def delete_account_cmd(message):
|
|
233
|
+
"""حذف یک اکانت خاص بر اساس شماره"""
|
|
234
|
+
try:
|
|
235
|
+
parts = message.text.split()
|
|
236
|
+
if len(parts) < 2:
|
|
237
|
+
await message.reply('📞 مثال: `del +989123456789`')
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
phone_number = normalize_phone_number(parts[1])
|
|
241
|
+
if not phone_number:
|
|
242
|
+
await message.reply("⚠️ شماره تلفن معتبر نیست.")
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
session_path = os.path.join(ACCOUNTS_FOLDER, f'{phone_number}.session')
|
|
246
|
+
json_path = os.path.join(ACCOUNTS_DATA_FOLDER, f'{phone_number}.json')
|
|
247
|
+
|
|
248
|
+
remove_client_from_pool(phone_number)
|
|
249
|
+
deleted = 0
|
|
250
|
+
|
|
251
|
+
for path in (session_path, json_path):
|
|
252
|
+
if os.path.isfile(path):
|
|
253
|
+
os.unlink(path)
|
|
254
|
+
deleted += 1
|
|
255
|
+
|
|
256
|
+
if deleted:
|
|
257
|
+
await message.reply(f'✅ اکانت {phone_number} حذف شد.')
|
|
258
|
+
else:
|
|
259
|
+
await message.reply(f'⚠️ اکانت {phone_number} یافت نشد.')
|
|
260
|
+
|
|
261
|
+
logger.info("%s: account deleted.", phone_number)
|
|
262
|
+
|
|
263
|
+
except Exception as e:
|
|
264
|
+
await message.reply(f'❌ خطا در حذف: {e}')
|
|
265
|
+
logger.error("delete_account_cmd error: %s", e)
|
|
266
|
+
|
|
267
|
+
# ============================================================
|
|
268
|
+
# 🔹 حذف تمام اکانتها
|
|
269
|
+
# ============================================================
|
|
270
|
+
async def delete_all_accounts_cmd(message):
|
|
271
|
+
"""حذف همه اکانتها از acc و acc_data"""
|
|
272
|
+
try:
|
|
273
|
+
accs = accounts()
|
|
274
|
+
if not accs:
|
|
275
|
+
await message.reply("⚠️ هیچ اکانتی برای حذف وجود ندارد.")
|
|
276
|
+
return
|
|
277
|
+
|
|
278
|
+
await stop_all_clients()
|
|
279
|
+
count = 0
|
|
280
|
+
|
|
281
|
+
for acc in accs:
|
|
282
|
+
for path in (
|
|
283
|
+
os.path.join(ACCOUNTS_FOLDER, f"{acc}.session"),
|
|
284
|
+
os.path.join(ACCOUNTS_DATA_FOLDER, f"{acc}.json")
|
|
285
|
+
):
|
|
286
|
+
if os.path.exists(path):
|
|
287
|
+
os.unlink(path)
|
|
288
|
+
count += 1
|
|
289
|
+
|
|
290
|
+
await message.reply(f"🧹 {count} فایل مربوط به اکانتها حذف شد.")
|
|
291
|
+
logger.info("All accounts deleted. total=%d", count)
|
|
292
|
+
|
|
293
|
+
except Exception as e:
|
|
294
|
+
await message.reply(f'❌ خطا در حذف همه اکانتها: {e}')
|
|
295
|
+
logger.error("delete_all_accounts_cmd error: %s", e)
|