Opera8 commited on
Commit
881a092
·
verified ·
1 Parent(s): 1914f86

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +85 -59
main.py CHANGED
@@ -93,7 +93,7 @@ def gregorian_to_jalali(gy, gm, gd):
93
  return jy, jm, jd
94
 
95
  # ==============================================================================
96
- # 🟢 پارت 4: دیتابیس SQLite (نسخه فوق‌سریع v4 + سیستم نجات خودکار از خطای Malformed)
97
  # ==============================================================================
98
  import os
99
  import sqlite3
@@ -101,73 +101,90 @@ import json
101
  import copy
102
  import threading
103
 
104
- DB_FILE = "/data/users_v4.db" # 🟢 دیتابیس کاملا جدید و سالم
105
- CORRUPT_V3_FILE = "/data/users_v3.db" # فایل ۳۰ مگابایتی که فهرستش خراب شده
106
  BACKUP_JSON = "/data/users_db.json.bak"
107
 
108
  last_saved_state = {}
 
109
  recent_messages_dict = {}
 
110
  db_lock = threading.Lock()
111
  msg_cache_lock = threading.Lock()
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  def init_sqlite_db():
114
  try:
115
  os.makedirs(os.path.dirname(DB_FILE), exist_ok=True)
116
  is_first_run = not os.path.exists(DB_FILE)
117
 
118
- # ساخت دیتابیس جدید
119
  conn = sqlite3.connect(DB_FILE, timeout=60.0)
120
- cursor = conn.cursor()
121
-
122
- # 🚀 تنظیمات سرعت دیتابیس (بدون دستور خطرناک VACUUM)
123
- cursor.execute('PRAGMA journal_mode=WAL;')
124
- cursor.execute('PRAGMA synchronous=NORMAL;')
125
- cursor.execute('CREATE TABLE IF NOT EXISTS users (chat_id TEXT PRIMARY KEY, user_data TEXT)')
126
- conn.commit()
127
- conn.close()
 
 
 
 
 
 
 
 
 
 
128
 
129
  if is_first_run:
130
- print("🚨 دیتابیس v4 ساخته شد. عملیات نجات داده‌ها آغاز شد...")
131
- rescue_data_from_corrupt_db()
132
 
133
  except Exception as e:
134
  print(f"❌ خطا در راه اندازی دیتابیس: {e}")
135
 
136
- def rescue_data_from_corrupt_db():
137
- """این تابع تلاش می‌کند داده‌ها را از فایل خراب v3 استخراج کرده و ب�� v4 بریزد"""
138
- if os.path.exists(CORRUPT_V3_FILE):
139
- print("🔍 در حال خواندن اطلاعات از فایل ۳۰ مگابایتی...")
140
- try:
141
- # اتصال به فایل سالم
142
- conn_new = sqlite3.connect(DB_FILE, timeout=60.0)
143
- conn_new.execute('PRAGMA journal_mode=WAL;')
144
-
145
- # اتصال موقت به فایل خراب
146
- conn_old = sqlite3.connect(CORRUPT_V3_FILE, timeout=60.0)
147
- cursor_old = conn_old.cursor()
148
-
149
- # بیرون کشیدن داده‌ها (حتی اگر دیتابیس اخطار بدهد، ردیف‌های سالم را می‌خواند)
150
- cursor_old.execute("SELECT chat_id, user_data FROM users")
151
- rows = cursor_old.fetchall()
152
-
153
- if rows:
154
- conn_new.executemany("INSERT OR IGNORE INTO users VALUES (?, ?)", rows)
155
- conn_new.commit()
156
- print(f"✅ موفقیت‌آمیز! تعداد {len(rows)} کاربر از دیتابیس خراب نجات یافتند.")
157
-
158
- conn_old.close()
159
- conn_new.close()
160
-
161
- # تغییر نام فایل خراب تا در ری‌استارت‌های بعدی دوباره پردازش نشود
162
- os.rename(CORRUPT_V3_FILE, "/data/users_v3_corrupt.bak")
163
-
164
- except Exception as e:
165
- print(f"⚠️ اخطار در حین نجات: {e}")
166
- print("در حال تلاش برای بازیابی از فایل JSON...")
167
- restore_users_from_json()
168
-
169
  def restore_users_from_json():
170
  if os.path.exists(BACKUP_JSON):
 
171
  try:
172
  with open(BACKUP_JSON, "r", encoding="utf-8") as f:
173
  data = json.load(f)
@@ -178,9 +195,9 @@ def restore_users_from_json():
178
  conn.execute("INSERT OR IGNORE INTO users VALUES (?, ?)", (str(cid), json.dumps(udata)))
179
  conn.commit()
180
  conn.close()
181
- print(f"✅ اعتبار {len(data)} کاربر از JSON بازیابی شد.")
182
  except Exception as e:
183
- print(f"❌ خطا در بازیابی از JSON: {e}")
184
 
185
  def load_db():
186
  global last_saved_state, recent_messages_dict
@@ -191,16 +208,17 @@ def load_db():
191
  conn.execute('PRAGMA journal_mode=WAL;')
192
  cursor = conn.cursor()
193
 
 
194
  cursor.execute("SELECT chat_id, user_data FROM users")
195
  for row in cursor.fetchall():
196
  db_dict[row[0]] = json.loads(row[1])
197
 
198
  conn.close()
199
  last_saved_state = copy.deepcopy(db_dict)
200
- print(f"🚀 دیتابیس آماده شد. تعداد کاربران: {len(db_dict)}")
201
  return db_dict
202
  except Exception as e:
203
- print(f"⚠️ ارور در لود نهایی دیتابیس: {e}")
204
  return {}
205
 
206
  # --- سیستم ذخیره سازی کاربران در پس‌زمینه ---
@@ -213,8 +231,13 @@ def background_save_worker(changed_data):
213
  conn.executemany("INSERT OR REPLACE INTO users (chat_id, user_data) VALUES (?, ?)", changed_data)
214
  conn.commit()
215
  conn.close()
 
 
 
 
 
216
  except Exception as e:
217
- pass # سکوت در برابر خطاهای جزئی پسزمینه
218
 
219
  def save_db(db_data):
220
  global last_saved_state
@@ -225,6 +248,7 @@ def save_db(db_data):
225
 
226
  if not changed: return
227
 
 
228
  for cid, _ in changed:
229
  last_saved_state[cid] = copy.deepcopy(db_data[cid])
230
 
@@ -242,8 +266,6 @@ def mark_message_processed(message_id):
242
  oldest_key = next(iter(recent_messages_dict))
243
  del recent_messages_dict[oldest_key]
244
 
245
- user_credits_db = load_db()
246
-
247
  # ==============================================================================
248
  # 🟢 پارت 5: توابع کد معرف، مدیریت سهمیه حساب کاربری و تبدیل اعداد
249
  # ==============================================================================
@@ -2144,12 +2166,12 @@ else:
2144
  except Exception:
2145
  pass
2146
 
2147
- # پیام چه قدیمی باشد چه جدید، در دیتابیس ثبت می‌شود که دیگر خوانده نشود
2148
- mark_message_processed(str_msg_id)
2149
-
2150
  if is_old:
2151
- # پیام تلنبار شده و قدیمی است، کاملا بیسروصدا دور ریخته می‌شود (بدون جواب دادن)
2152
  return
 
 
 
2153
  # ===================================================================
2154
 
2155
  user_text = getattr(update, "text", "") or getattr(msg_obj, "text", "")
@@ -2324,7 +2346,11 @@ else:
2324
  payload = {"chat_id": chat_id, "text": join_msg, "chat_keypad_type": "New", "chat_keypad": JOIN_KEYPAD_DICT}
2325
  await client._make_request("sendMessage", payload)
2326
  except Exception:
2327
- await client.send_message(chat_id, join_msg)
 
 
 
 
2328
  return
2329
 
2330
  if user_text_lower.startswith("/start"):
 
93
  return jy, jm, jd
94
 
95
  # ==============================================================================
96
+ # 🟢 پارت 4: دیتابیس SQLite (نسخه مجهز به موتور تعمیر خودکار و استخراج داده)
97
  # ==============================================================================
98
  import os
99
  import sqlite3
 
101
  import copy
102
  import threading
103
 
104
+ DB_FILE = "/data/users_v3.db"
 
105
  BACKUP_JSON = "/data/users_db.json.bak"
106
 
107
  last_saved_state = {}
108
+ # حافظه کش فقط در RAM (با ری‌استارت اسپیس پاک می‌شود)
109
  recent_messages_dict = {}
110
+ # قفل‌ها برای جلوگیری از تداخل رشته‌ها (Threads)
111
  db_lock = threading.Lock()
112
  msg_cache_lock = threading.Lock()
113
 
114
+ def auto_repair_database():
115
+ print("🛠 در حال بازسازی فایل دیتابیس آسیب‌دیده...")
116
+ corrupt_backup = DB_FILE + ".corrupt"
117
+
118
+ # 1. تغییر نام فایل خراب به فایل بکاپ
119
+ for ext in["", "-wal", "-shm"]:
120
+ if os.path.exists(DB_FILE + ext):
121
+ try:
122
+ if ext == "":
123
+ if os.path.exists(corrupt_backup): os.remove(corrupt_backup)
124
+ os.rename(DB_FILE, corrupt_backup)
125
+ else:
126
+ os.remove(DB_FILE + ext)
127
+ except Exception: pass
128
+
129
+ # 2. ساخت دیتابیس تمیز و جدید
130
+ conn = sqlite3.connect(DB_FILE, timeout=60.0)
131
+ conn.execute('PRAGMA journal_mode=WAL;')
132
+ conn.execute('CREATE TABLE IF NOT EXISTS users (chat_id TEXT PRIMARY KEY, user_data TEXT)')
133
+
134
+ # 3. تلاش برای استخراج تمام داده‌های سالم از فایل خراب به دیتابیس جدید
135
+ recovered = 0
136
+ if os.path.exists(corrupt_backup):
137
+ try:
138
+ conn.execute(f"ATTACH DATABASE '{corrupt_backup}' AS old_db")
139
+ conn.execute("INSERT OR IGNORE INTO users SELECT * FROM old_db.users")
140
+ conn.commit()
141
+ cursor = conn.cursor()
142
+ cursor.execute("SELECT COUNT(*) FROM users")
143
+ recovered = cursor.fetchone()[0]
144
+ conn.execute("DETACH DATABASE old_db")
145
+ except Exception as e:
146
+ print(f"⚠️ استخراج با خطا مواجه شد، اما داده‌های سالم نجات یافتند: {e}")
147
+
148
+ conn.close()
149
+ print(f"✅ فایل بازسازی شد! تعداد {recovered} کاربر با موفقیت از فایل آسیب‌دیده استخراج شدند.")
150
+
151
+ # 4. در نهایت از فایل متنی JSON هم بازیابی می‌کنیم تا هیچکس جا نماند
152
+ restore_users_from_json()
153
+
154
  def init_sqlite_db():
155
  try:
156
  os.makedirs(os.path.dirname(DB_FILE), exist_ok=True)
157
  is_first_run = not os.path.exists(DB_FILE)
158
 
 
159
  conn = sqlite3.connect(DB_FILE, timeout=60.0)
160
+ try:
161
+ # 🚀 تنظیمات سرعت دیتابیس
162
+ conn.execute('PRAGMA journal_mode=WAL;')
163
+ conn.execute('PRAGMA synchronous=NORMAL;')
164
+ conn.execute('CREATE TABLE IF NOT EXISTS users (chat_id TEXT PRIMARY KEY, user_data TEXT)')
165
+ conn.execute('DROP TABLE IF EXISTS processed_messages')
166
+ conn.commit()
167
+ conn.execute('VACUUM')
168
+ except sqlite3.DatabaseError as e:
169
+ if "malformed" in str(e).lower():
170
+ # اگر دیتابیس خراب بود، موتور تعمیر خودکار روشن میشود
171
+ conn.close()
172
+ auto_repair_database()
173
+ return
174
+ except Exception: pass
175
+ finally:
176
+ try: conn.close()
177
+ except: pass
178
 
179
  if is_first_run:
180
+ restore_users_from_json()
 
181
 
182
  except Exception as e:
183
  print(f"❌ خطا در راه اندازی دیتابیس: {e}")
184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  def restore_users_from_json():
186
  if os.path.exists(BACKUP_JSON):
187
+ print("📥 در حال بازیابی اعتبار کاربران از فایل پشتیبان JSON...")
188
  try:
189
  with open(BACKUP_JSON, "r", encoding="utf-8") as f:
190
  data = json.load(f)
 
195
  conn.execute("INSERT OR IGNORE INTO users VALUES (?, ?)", (str(cid), json.dumps(udata)))
196
  conn.commit()
197
  conn.close()
198
+ print(f"✅ اعتبار {len(data)} کاربر از فایل JSON بازیابی شد.")
199
  except Exception as e:
200
+ print(f"❌ خطا در بازیابی اعتبارها: {e}")
201
 
202
  def load_db():
203
  global last_saved_state, recent_messages_dict
 
208
  conn.execute('PRAGMA journal_mode=WAL;')
209
  cursor = conn.cursor()
210
 
211
+ # 🟢 فقط لود کردن اطلاعات کاربران
212
  cursor.execute("SELECT chat_id, user_data FROM users")
213
  for row in cursor.fetchall():
214
  db_dict[row[0]] = json.loads(row[1])
215
 
216
  conn.close()
217
  last_saved_state = copy.deepcopy(db_dict)
218
+ print(f"🚀 دیتابیس آماده شد. تعداد کاربران: {len(db_dict)} | کش پیام‌ها پاکسازی شد.")
219
  return db_dict
220
  except Exception as e:
221
+ print(f"⚠️ ارور در لود نهایی: {e}")
222
  return {}
223
 
224
  # --- سیستم ذخیره سازی کاربران در پس‌زمینه ---
 
231
  conn.executemany("INSERT OR REPLACE INTO users (chat_id, user_data) VALUES (?, ?)", changed_data)
232
  conn.commit()
233
  conn.close()
234
+ except sqlite3.DatabaseError as e:
235
+ # مانیتورینگ زنده: اگر وسط کار فایل خراب شد سریعاً تعمیر می‌شود
236
+ if "malformed" in str(e).lower():
237
+ print("❌ دیتابیس هنگام ذخیره خراب شد! در حال تعمیر خودکار...")
238
+ auto_repair_database()
239
  except Exception as e:
240
+ print(f"❌ خطا در ذخیره بکگراند: {e}")
241
 
242
  def save_db(db_data):
243
  global last_saved_state
 
248
 
249
  if not changed: return
250
 
251
+ # آپدیت وضعیت استیت محلی
252
  for cid, _ in changed:
253
  last_saved_state[cid] = copy.deepcopy(db_data[cid])
254
 
 
266
  oldest_key = next(iter(recent_messages_dict))
267
  del recent_messages_dict[oldest_key]
268
 
 
 
269
  # ==============================================================================
270
  # 🟢 پارت 5: توابع کد معرف، مدیریت سهمیه حساب کاربری و تبدیل اعداد
271
  # ==============================================================================
 
2166
  except Exception:
2167
  pass
2168
 
 
 
 
2169
  if is_old:
2170
+ # پیام‌های تلنبار شده و قدیمی اصلا در دیتابیس و حافظه ذخیره نمی‌شوند و درجا دور ریخته می‌شوند
2171
  return
2172
+
2173
+ # ربات فقط شناسه‌های پیام‌های کاملاً جدید را در کش رم ذخیره می‌کند
2174
+ mark_message_processed(str_msg_id)
2175
  # ===================================================================
2176
 
2177
  user_text = getattr(update, "text", "") or getattr(msg_obj, "text", "")
 
2346
  payload = {"chat_id": chat_id, "text": join_msg, "chat_keypad_type": "New", "chat_keypad": JOIN_KEYPAD_DICT}
2347
  await client._make_request("sendMessage", payload)
2348
  except Exception:
2349
+ # 🔴 رفع خطای مسدودیت: اگر کاربر ربات را بلاک کرده باشد و خطا دهد، ربات با آرامش عبور میکند.
2350
+ try:
2351
+ await client.send_message(chat_id, join_msg)
2352
+ except Exception:
2353
+ pass
2354
  return
2355
 
2356
  if user_text_lower.startswith("/start"):