CliRemote 1.7.10__py3-none-any.whl → 1.7.13__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: CliRemote
3
- Version: 1.7.10
3
+ Version: 1.7.13
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,8 +1,8 @@
1
- cliremote-1.7.10.dist-info/licenses/LICENSE,sha256=O-0zMbcEi6wXz1DiSdVgzMlQjJcNqNe5KDv08uYzqR0,1055
1
+ cliremote-1.7.13.dist-info/licenses/LICENSE,sha256=O-0zMbcEi6wXz1DiSdVgzMlQjJcNqNe5KDv08uYzqR0,1055
2
2
  remote/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  remote/account_manager.py,sha256=TepnGIoE2hU-j_NmU5ByoS-JrboE0w3A1bYmQoQX5h8,15176
4
4
  remote/account_viewer.py,sha256=j46KSjbgrBmBi7UxFeJ5tCwHIe0QvCvphkirGIbB2oo,5192
5
- remote/admin_manager.py,sha256=lgpdN3Mo6YzxNNm7eb7fjGuKzzkV39VRGafpavrAp8Y,4802
5
+ remote/admin_manager.py,sha256=JXxZ6dEF5xXUcNmLmxuFAAUE7EyHDf0sLutTQYnTlHY,7392
6
6
  remote/analytics_manager.py,sha256=6jPvwt_ELA4RMbQdD8W_ltfAoaSgILnEkOAp6HZAqsU,7382
7
7
  remote/batch_manager.py,sha256=jVGhYVwHMKJd7f7JxcWjKlwr03dq0RaGD1KdkyYdb00,1051
8
8
  remote/block_manager.py,sha256=R7UaQigr-hTRtjxjG3OvJdKhvp0mDpLaESp3Of1AYhs,5692
@@ -33,7 +33,7 @@ remote/text_manager.py,sha256=C2wNSXPSCDu8NSD3RsfbKmUQMWOYd1B5N4tzy-Jsriw,2195
33
33
  remote/username_manager.py,sha256=nMNdke-2FIv86xR1Y6rR-43oUoQu_3Khw8wEo54noXI,3388
34
34
  remote/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  remote/utils/sqlite_utils.py,sha256=5i0oUXsBgKC_8qHZPJ-Gyhp9D1TwqKHVvuZRIhKpS6w,1260
36
- cliremote-1.7.10.dist-info/METADATA,sha256=QTB61ZPptktRQaQTzGvRU5-F5vX4JatOmWdT_STGw1Y,1203
37
- cliremote-1.7.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
- cliremote-1.7.10.dist-info/top_level.txt,sha256=yBZidJ6zCix_a2ubGlYaewvlzBFXWbckQt20dudxJ1E,7
39
- cliremote-1.7.10.dist-info/RECORD,,
36
+ cliremote-1.7.13.dist-info/METADATA,sha256=AGkWN4uIhUNz55bPpKmv6t31CJvf53AS2l3uuMsjlJg,1203
37
+ cliremote-1.7.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
+ cliremote-1.7.13.dist-info/top_level.txt,sha256=yBZidJ6zCix_a2ubGlYaewvlzBFXWbckQt20dudxJ1E,7
39
+ cliremote-1.7.13.dist-info/RECORD,,
remote/admin_manager.py CHANGED
@@ -1,67 +1,99 @@
1
1
  # remote/admin_manager.py
2
- import json, os, sys, logging
2
+ import json
3
+ import os
4
+ import sys
5
+ import logging
3
6
  from pathlib import Path
4
7
  from pyrogram import filters
5
8
  from .config import OWNER_ID
6
9
 
7
- logger = logging.getLogger(__name__)
10
+ # =============================
11
+ # تنظیم logger فایل
12
+ # =============================
8
13
 
9
14
  def _project_root() -> Path:
10
- """
11
- ریشه پروژه = پوشه‌ای که main.py داخلش اجرا شده.
12
- """
15
+ """ریشه پروژه = پوشه‌ای که main.py داخلش اجرا شده. در صورت عدم دسترسی، از cwd استفاده می‌شود."""
13
16
  try:
14
17
  main_file = Path(sys.modules["__main__"].__file__).resolve()
15
18
  return main_file.parent
16
19
  except Exception:
17
- # fallback: اگر به هر دلیل __main__.__file__ نبود
18
20
  return Path(os.getcwd()).resolve()
19
21
 
20
- PROJECT_ROOT = _project_root()
21
- ADMINS_FILE = PROJECT_ROOT / "admins.json" # ✅ کنار main.py
22
+ _PROJECT_ROOT = _project_root()
23
+ _LOG_DIR = _PROJECT_ROOT / "logs"
24
+ _LOG_DIR.mkdir(parents=True, exist_ok=True)
25
+ _LOG_PATH = _LOG_DIR / "admins_log.txt"
26
+
27
+ # logger اختصاصی این ماژول
28
+ logger = logging.getLogger("remote.admin_manager")
29
+ logger.setLevel(logging.DEBUG)
30
+
31
+ # جلوگیری از افزودن چندباره‌ی هندلر هنگام ریلود
32
+ if not any(isinstance(h, logging.FileHandler) and getattr(h, "_admin_log", False) for h in logger.handlers):
33
+ fh = logging.FileHandler(_LOG_PATH, encoding="utf-8")
34
+ fh._admin_log = True # پرچم داخلی برای تشخیص
35
+ fh.setLevel(logging.DEBUG)
36
+ fmt = logging.Formatter(
37
+ fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
38
+ datefmt="%Y-%m-%d %H:%M:%S"
39
+ )
40
+ fh.setFormatter(fmt)
41
+ logger.addHandler(fh)
42
+
43
+ logger.debug(f"Admin manager initialized. Log path: {_LOG_PATH}")
44
+
45
+ # =============================
46
+ # تنظیمات فایل ادمین‌ها
47
+ # =============================
48
+
49
+ ADMINS_FILE = "admins.json" # اگر می‌خواهی کنار main.py باشد: ( _PROJECT_ROOT / "admins.json" ).as_posix()
22
50
 
23
- def _load_admins_from_file() -> list[int]:
51
+ def load_admins() -> list[int]:
52
+ """
53
+ بارگذاری لیست ادمین‌ها از فایل.
54
+ همیشه OWNER_ID را هم به لیست اضافه می‌کند.
55
+ """
56
+ logger.debug(f"Loading admins from file: {ADMINS_FILE} | OWNER_ID: {OWNER_ID}")
57
+ s = set(OWNER_ID)
24
58
  try:
25
- if ADMINS_FILE.exists():
26
- with ADMINS_FILE.open("r", encoding="utf-8") as f:
59
+ if os.path.exists(ADMINS_FILE):
60
+ with open(ADMINS_FILE, "r", encoding="utf-8") as f:
27
61
  data = json.load(f)
62
+ logger.info(f"admins.json loaded. Raw: {data!r}")
28
63
  if isinstance(data, list):
29
- out = []
30
64
  for v in data:
31
65
  try:
32
- out.append(int(v))
33
- except Exception:
34
- logger.warning(f"Bad admin id in file: {v!r}")
35
- return out
66
+ s.add(int(v))
67
+ except Exception as conv_err:
68
+ logger.warning(f"Skip invalid admin id in file: {v!r} | err={conv_err}")
69
+ else:
70
+ logger.info(f"admins.json not found at: {os.path.abspath(ADMINS_FILE)}")
36
71
  except Exception as e:
37
- logger.warning(f"Error loading admins from {ADMINS_FILE}: {e}")
38
- return []
72
+ logger.warning(f"Error loading admins: {e}", exc_info=True)
39
73
 
40
- # لیست ادمین‌های موثر (فایل + Owner)
41
- ADMINS: list[int] = []
74
+ result = sorted(s)
75
+ logger.debug(f"Effective ADMINS after merge with OWNER_ID: {result}")
76
+ return result
42
77
 
43
- def reload_admins():
44
- """فایل را می‌خواند و با OWNER_ID ادغام می‌کند؛ نتیجه در ADMINS."""
45
- file_admins = _load_admins_from_file()
46
- s = set(file_admins) | set(OWNER_ID)
47
- global ADMINS
48
- ADMINS = sorted(s)
49
- logger.info(f"Loaded admins ({ADMINS_FILE}): {ADMINS}")
50
78
 
51
79
  def save_admins():
52
80
  """
53
- ذخیره در فایل کنار main.py.
54
- فقط ادمین‌های غیر-Owner را داخل فایل نگه می‌داریم (Ownerها از config می‌آیند).
81
+ ذخیره‌ی ادمین‌ها در فایل.
55
82
  """
56
83
  try:
57
- file_list = [x for x in ADMINS if x not in set(OWNER_ID)]
58
- with ADMINS_FILE.open("w", encoding="utf-8") as f:
59
- json.dump(file_list, f, ensure_ascii=False, indent=2)
60
- logger.info(f"Saved admins to {ADMINS_FILE}: {file_list}")
84
+ # نکته: در این طراحی، ADMINS شامل OWNER_ID هم می‌تواند باشد؛ مشکلی نیست
85
+ logger.debug(f"Saving ADMINS to file: {ADMINS_FILE} | Data: {ADMINS}")
86
+ with open(ADMINS_FILE, "w", encoding="utf-8") as f:
87
+ json.dump(list(ADMINS), f, ensure_ascii=False, indent=2)
88
+ logger.info(f"Admins saved to {os.path.abspath(ADMINS_FILE)}")
61
89
  except Exception as e:
62
- logger.error(f"Error saving admins: {e}")
90
+ logger.error(f"Error saving admins: {e}", exc_info=True)
63
91
 
64
- # فیلترهای دسترسی
92
+
93
+ ADMINS = load_admins()
94
+ logger.info(f"Loaded admins at import time: {ADMINS}")
95
+
96
+ # فیلترهای دسترسی برای Pyrogram
65
97
  admin_filter = filters.create(
66
98
  lambda _, __, m: bool(getattr(m, "from_user", None)) and int(m.from_user.id) in ADMINS
67
99
  )
@@ -69,57 +101,87 @@ owner_filter = filters.create(
69
101
  lambda _, __, m: bool(getattr(m, "from_user", None)) and int(m.from_user.id) in OWNER_ID
70
102
  )
71
103
 
72
- # ===== فرمان‌ها =====
104
+ # =============================
105
+ # فرمان‌های مدیریتی
106
+ # =============================
107
+
73
108
  async def add_admin_cmd(message):
74
109
  try:
110
+ uid_display = getattr(getattr(message, "from_user", None), "id", None)
111
+ logger.debug(f"add_admin_cmd triggered by user_id={uid_display} | text={message.text!r}")
112
+
75
113
  parts = (message.text or "").split()
76
114
  if len(parts) < 2:
77
- await message.reply("مثال: /addadmin 123456789")
115
+ logger.debug("add_admin_cmd: missing argument")
116
+ await message.reply("مثال: addadmin 123456789")
78
117
  return
118
+
79
119
  uid = int(parts[1])
120
+ logger.debug(f"add_admin_cmd: parsed target uid={uid}")
121
+
80
122
  if uid in OWNER_ID:
123
+ logger.info(f"add_admin_cmd: uid={uid} is OWNER; skip append")
81
124
  await message.reply("ادمین اصلی از قبل وجود دارد")
82
125
  return
126
+
83
127
  if uid not in ADMINS:
84
128
  ADMINS.append(uid)
85
- ADMINS[:] = sorted(set(ADMINS) | set(OWNER_ID))
129
+ logger.info(f"Admin appended: {uid} | New ADMINS={sorted(ADMINS)}")
86
130
  save_admins()
87
- await message.reply(f"ادمین جدید اضافه شد: <code>{uid}</code>")
88
- logger.info(f"Admin added: {uid}")
131
+ await message.reply(f"ادمین جدید اضافه شد: {uid}")
89
132
  else:
133
+ logger.info(f"add_admin_cmd: uid={uid} already in ADMINS")
90
134
  await message.reply("قبلاً ادمین بود")
91
135
  except Exception as e:
92
136
  logger.error(f"add_admin_cmd error: {e}", exc_info=True)
93
137
  await message.reply(f"خطا: {e}")
94
138
 
139
+
95
140
  async def del_admin_cmd(message):
96
141
  try:
142
+ uid_display = getattr(getattr(message, "from_user", None), "id", None)
143
+ logger.debug(f"del_admin_cmd triggered by user_id={uid_display} | text={message.text!r}")
144
+
97
145
  parts = (message.text or "").split()
98
146
  if len(parts) < 2:
99
- await message.reply("مثال: /deladmin 123456789")
147
+ logger.debug("del_admin_cmd: missing argument")
148
+ await message.reply("مثال: deladmin 123456789")
100
149
  return
150
+
101
151
  uid = int(parts[1])
152
+ logger.debug(f"del_admin_cmd: parsed target uid={uid}")
153
+
102
154
  if uid in OWNER_ID:
155
+ logger.info(f"del_admin_cmd: attempt to remove OWNER uid={uid} blocked")
103
156
  await message.reply("❌ امکان حذف ادمین اصلی وجود ندارد")
104
157
  return
158
+
105
159
  if uid in ADMINS:
106
160
  ADMINS.remove(uid)
161
+ logger.info(f"Admin removed: {uid} | New ADMINS={sorted(ADMINS)}")
107
162
  save_admins()
108
- await message.reply(f"ادمین حذف شد: <code>{uid}</code>")
109
- logger.info(f"Admin removed: {uid}")
163
+ await message.reply(f"ادمین حذف شد: {uid}")
110
164
  else:
165
+ logger.info(f"del_admin_cmd: uid={uid} not in ADMINS")
111
166
  await message.reply("کاربر ادمین نیست")
112
167
  except Exception as e:
113
168
  logger.error(f"del_admin_cmd error: {e}", exc_info=True)
114
169
  await message.reply(f"خطا: {e}")
115
170
 
171
+
116
172
  async def list_admins_cmd(message):
117
173
  try:
174
+ uid_display = getattr(getattr(message, "from_user", None), "id", None)
175
+ logger.debug(f"list_admins_cmd triggered by user_id={uid_display}")
176
+
118
177
  if not ADMINS:
178
+ logger.info("list_admins_cmd: ADMINS is empty")
119
179
  await message.reply("لیست ادمین‌ها خالی است.")
120
180
  return
121
- text = "👑 <b>ADMINS:</b>\n" + "\n".join([f"<code>{x}</code>" for x in ADMINS])
122
- await message.reply(text, disable_web_page_preview=True)
181
+
182
+ text = "👑 <b>ADMINS:</b>\n" + "\n".join([str(x) for x in sorted(ADMINS)])
183
+ logger.debug(f"list_admins_cmd: respond with {len(ADMINS)} admins")
184
+ await message.reply(text)
123
185
  except Exception as e:
124
186
  logger.error(f"list_admins_cmd error: {e}", exc_info=True)
125
187
  await message.reply(f"خطا: {e}")