Opera8 commited on
Commit
624fa1f
·
verified ·
1 Parent(s): c4c9ca6

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +76 -95
main.py CHANGED
@@ -92,163 +92,144 @@ def gregorian_to_jalali(gy, gm, gd):
92
  return jy, jm, jd
93
 
94
  # ==============================================================================
95
- # 🟢 پارت 4: راه‌اندازی دیتابیس هوشمند و پرسرعت SQLite + انتقال خودکار کاربران قدیمی
96
  # ==============================================================================
97
  import os
98
  import sqlite3
99
  import json
100
  import copy
101
 
102
- # آدرس فایل‌های دیتابیس در پوشه Mount شده فضای ذخیره‌سازی ابری
103
  DB_FILE = "/data/users_db.db"
104
- OLD_JSON_FILE = "/data/users_db.json"
105
 
106
- # این متغیر برای نگهداری آخرین وضعیت کاربران استفاده می‌شود تا فقط تغییرات جدید در دیتابیس نوشته شوند
107
  last_saved_state = {}
108
 
109
  def init_sqlite_db():
110
- """ساخت جداول دیتابیس در صورت عدم وجود و فعال‌سازی حالت پرسرعت WAL"""
111
  try:
112
- # اطمینان از وجود پوشه data
113
  os.makedirs(os.path.dirname(DB_FILE), exist_ok=True)
114
 
115
- conn = sqlite3.connect(DB_FILE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  cursor = conn.cursor()
117
 
118
- # فعال‌سازی WAL (Write-Ahead Logging) برای جلوگیری از قفل شدن (Lock) دیتابیس
119
- cursor.execute('PRAGMA journal_mode=WAL;')
120
-
121
- # جدول کاربران
122
- cursor.execute('''
123
- CREATE TABLE IF NOT EXISTS users (
124
- chat_id TEXT PRIMARY KEY,
125
- user_data TEXT
126
- )
127
- ''')
128
 
129
- # جدول شناسه‌های پیام پردازش شده (ضد تکرار قطعی پیام)
130
- cursor.execute('''
131
- CREATE TABLE IF NOT EXISTS processed_messages (
132
- message_id TEXT PRIMARY KEY
133
- )
134
- ''')
135
 
136
  conn.commit()
137
  conn.close()
 
138
  except Exception as e:
139
- print("خطا در ساخت دیتابیس SQLite:", e)
140
 
141
- def migrate_old_json_to_sqlite():
142
- """انتقال خودکار و امن اطلاعات فایل قدیمی JSON (اشتراک‌ها و سکهها) به دیتابیس جدید SQLite"""
143
- if os.path.exists(OLD_JSON_FILE):
144
- print("⚠️ در حال انتقال اطلاعات کاربران از فایل JSON قدیمی به دیتابیس SQLite...")
145
  try:
146
- with open(OLD_JSON_FILE, "r", encoding="utf-8") as f:
147
- old_data = json.load(f)
148
-
149
- if old_data:
150
- conn = sqlite3.connect(DB_FILE)
151
- cursor = conn.cursor()
152
-
153
- # انتقال تمام کاربران قدیمی به دیتابیس جدید (بدون پاک شدن اشتراک‌ها)
154
- for chat_id, data in old_data.items():
155
- user_data_str = json.dumps(data, ensure_ascii=False)
156
- cursor.execute("INSERT OR IGNORE INTO users (chat_id, user_data) VALUES (?, ?)", (str(chat_id), user_data_str))
157
-
158
  conn.commit()
159
  conn.close()
160
- print(f"✅ انتقال اطلاعات {len(old_data)} کاربر قدیمی با موفقیت انجام شد.")
161
-
162
- # تغییر نام فایل قدیمی به نسخه پشتیبان (.bak) تا دیگر خوانده نشود و امنیت حفظ شود
163
- backup_file = OLD_JSON_FILE + ".bak"
164
- os.rename(OLD_JSON_FILE, backup_file)
165
- print(f"✅ فایل قدیمی به {backup_file} تغییر نام یافت.")
166
  except Exception as e:
167
- print(f"❌ خطا در انتقال اطلاعات فایل JSON قدیمی: {e}")
168
 
169
  def load_db():
170
- """لود کردن اطلاعات کاربران از SQLite به داخل حافظه رم ربات در لحظه استارت"""
171
  global last_saved_state
172
- print(f"در حال خواندن دیتابیس SQLite از مسیر {DB_FILE} ...")
173
-
174
  init_sqlite_db()
175
 
176
- # 🟢 فراخوانی سیستم انتقال خودکار (فقط یک‌بار اجرا می‌شود و اگر فایل قدیمی نباشد نادیده گرفته می‌شود)
177
- migrate_old_json_to_sqlite()
178
-
179
  db_dict = {}
180
  try:
181
- conn = sqlite3.connect(DB_FILE)
182
  cursor = conn.cursor()
 
 
 
 
 
 
 
 
 
183
  cursor.execute("SELECT chat_id, user_data FROM users")
184
- rows = cursor.fetchall()
185
- for row in rows:
186
- chat_id, user_data_str = row
187
- db_dict[chat_id] = json.loads(user_data_str)
188
- conn.close()
189
 
190
- # یک کپی دقیق از وضعیت اولیه می‌گیریم تا بعداً فقط تغییرات را تشخیص دهیم
191
  last_saved_state = copy.deepcopy(db_dict)
192
- print(f" دیتابیس SQLite با موفقیت لود شد. (تعداد کاربران فعلی: {len(db_dict)})")
193
  return db_dict
194
  except Exception as e:
195
- print(f"⚠️ خطا در خواندن دیتابیس: {e}")
196
  return {}
197
 
198
  def save_db(db_data):
199
- """ذخیره‌سازی هوشمند و فوق‌سریع کاربران"""
200
  global last_saved_state
201
  try:
202
- changed_records =[]
203
-
204
- # تشخیص کاربرانی که اطلاعاتشان تغییر کرده
205
- for chat_id, data in db_data.items():
206
- if chat_id not in last_saved_state or last_saved_state[chat_id] != data:
207
- user_data_str = json.dumps(data, ensure_ascii=False)
208
- changed_records.append((str(chat_id), user_data_str))
209
 
210
- if not changed_records:
211
- return
212
 
213
- conn = sqlite3.connect(DB_FILE, timeout=15.0)
214
- cursor = conn.cursor()
215
- cursor.execute('PRAGMA journal_mode=WAL;')
216
-
217
- cursor.executemany("INSERT OR REPLACE INTO users (chat_id, user_data) VALUES (?, ?)", changed_records)
218
  conn.commit()
219
  conn.close()
220
 
221
- for chat_id, _ in changed_records:
222
- last_saved_state[chat_id] = copy.deepcopy(db_data[chat_id])
223
-
224
  except Exception as e:
225
- print("خطا در آپدیت دیتابیس SQLite:", e)
226
 
227
  def is_message_processed(message_id):
228
- """بررسی سریع اینکه آیا پیام قبلاً در دیتابیس ثبت شده است یا خیر"""
229
  try:
230
- conn = sqlite3.connect(DB_FILE, timeout=10.0)
231
  cursor = conn.cursor()
232
  cursor.execute("SELECT 1 FROM processed_messages WHERE message_id = ?", (str(message_id),))
233
- result = cursor.fetchone()
234
  conn.close()
235
- return result is not None
236
- except Exception:
237
- return False
238
 
239
  def mark_message_processed(message_id):
240
- """ثبت همیشگی شناسه پیام در دیتابیس"""
241
  try:
242
- conn = sqlite3.connect(DB_FILE, timeout=10.0)
243
- cursor = conn.cursor()
244
- cursor.execute('PRAGMA journal_mode=WAL;')
245
- cursor.execute("INSERT OR IGNORE INTO processed_messages (message_id) VALUES (?)", (str(message_id),))
246
  conn.commit()
247
  conn.close()
248
- except Exception:
249
- pass
250
 
251
- # جایگذاری متغیر اصلی دیتابیس با مکانیزم جدید
252
  user_credits_db = load_db()
253
 
254
 
 
92
  return jy, jm, jd
93
 
94
  # ==============================================================================
95
+ # 🟢 پارت 4: دیتابیس SQLite سخه نهایی - ضد خرابی در شبکه Hugging Face)
96
  # ==============================================================================
97
  import os
98
  import sqlite3
99
  import json
100
  import copy
101
 
 
102
  DB_FILE = "/data/users_db.db"
103
+ BACKUP_JSON_FILE = "/data/users_db.json.bak"
104
 
 
105
  last_saved_state = {}
106
 
107
  def init_sqlite_db():
108
+ """راه‌اندازی دیتابیس با تنظیمات مخصوص درایوهای شبکه"""
109
  try:
 
110
  os.makedirs(os.path.dirname(DB_FILE), exist_ok=True)
111
 
112
+ # ۱. حذف فایل‌های موقت WAL که باعث خرابی می‌شوند
113
+ for ext in ["-wal", "-shm"]:
114
+ if os.path.exists(DB_FILE + ext):
115
+ try: os.remove(DB_FILE + ext)
116
+ except: pass
117
+
118
+ # ۲. بررسی سلامت و بازسازی در صورت خرابی
119
+ if os.path.exists(DB_FILE):
120
+ conn_test = sqlite3.connect(DB_FILE, timeout=10)
121
+ try:
122
+ conn_test.execute("PRAGMA integrity_check").fetchone()
123
+ conn_test.close()
124
+ except:
125
+ conn_test.close()
126
+ print("🚨 دیتابیس غیرقابل تعمیر است. در حال جایگزینی با فایل سالم...")
127
+ os.rename(DB_FILE, DB_FILE + ".old") # فایل خراب را کنار بگذار
128
+
129
+ # ۳. اتصال با تنظیمات امن (حالت TRUNCATE به جای WAL)
130
+ conn = sqlite3.connect(DB_FILE, timeout=60.0)
131
  cursor = conn.cursor()
132
 
133
+ # این تنظیمات برای Hugging Face حیاتی است:
134
+ cursor.execute('PRAGMA journal_mode=TRUNCATE;')
135
+ cursor.execute('PRAGMA synchronous=FULL;')
 
 
 
 
 
 
 
136
 
137
+ cursor.execute('CREATE TABLE IF NOT EXISTS users (chat_id TEXT PRIMARY KEY, user_data TEXT)')
138
+ cursor.execute('CREATE TABLE IF NOT EXISTS processed_messages (message_id TEXT PRIMARY KEY)')
 
 
 
 
139
 
140
  conn.commit()
141
  conn.close()
142
+ print("✅ دیتابیس با حالت امن شبکه (TRUNCATE) آماده شد.")
143
  except Exception as e:
144
+ print(f"خطا در راه اندازی دیتابیس: {e}")
145
 
146
+ def restore_credits_only():
147
+ """بازیابی اعتبار کاربران از بک‌آپ JSON"""
148
+ if os.path.exists(BACKUP_JSON_FILE):
149
+ print("🛠 در حال بازیابی اعتبارها از فایل بک‌آپ...")
150
  try:
151
+ with open(BACKUP_JSON_FILE, "r", encoding="utf-8") as f:
152
+ backup_data = json.load(f)
153
+ if backup_data:
154
+ conn = sqlite3.connect(DB_FILE, timeout=60.0)
155
+ for chat_id, data in backup_data.items():
156
+ conn.execute("INSERT OR REPLACE INTO users (chat_id, user_data) VALUES (?, ?)",
157
+ (str(chat_id), json.dumps(data, ensure_ascii=False)))
 
 
 
 
 
158
  conn.commit()
159
  conn.close()
160
+ print(f"✅ اعتبار {len(backup_data)} کاربر با موفقیت بازگشت.")
 
 
 
 
 
161
  except Exception as e:
162
+ print(f"❌ خطا در بازیابی: {e}")
163
 
164
  def load_db():
 
165
  global last_saved_state
 
 
166
  init_sqlite_db()
167
 
 
 
 
168
  db_dict = {}
169
  try:
170
+ conn = sqlite3.connect(DB_FILE, timeout=60.0)
171
  cursor = conn.cursor()
172
+
173
+ # چک کردن تعداد کاربران
174
+ cursor.execute("SELECT count(*) FROM users")
175
+ if cursor.fetchone()[0] < 50: # اگر دیتابیس خالی است، بازیابی کن
176
+ conn.close()
177
+ restore_credits_only()
178
+ conn = sqlite3.connect(DB_FILE, timeout=60.0)
179
+ cursor = conn.cursor()
180
+
181
  cursor.execute("SELECT chat_id, user_data FROM users")
182
+ for row in cursor.fetchall():
183
+ db_dict[row[0]] = json.loads(row[1])
 
 
 
184
 
185
+ conn.close()
186
  last_saved_state = copy.deepcopy(db_dict)
187
+ print(f"📊 لود نهایی: {len(db_dict)} کاربر آماده هستند.")
188
  return db_dict
189
  except Exception as e:
190
+ print(f"⚠️ خطا در لود نهایی: {e}")
191
  return {}
192
 
193
  def save_db(db_data):
 
194
  global last_saved_state
195
  try:
196
+ changed = []
197
+ for cid, data in db_data.items():
198
+ if cid not in last_saved_state or last_saved_state[cid] != data:
199
+ changed.append((str(cid), json.dumps(data, ensure_ascii=False)))
 
 
 
200
 
201
+ if not changed: return
 
202
 
203
+ # استفاده از TRUNCATE برای امنیت در شبکه
204
+ conn = sqlite3.connect(DB_FILE, timeout=60.0)
205
+ conn.execute('PRAGMA journal_mode=TRUNCATE;')
206
+ conn.executemany("INSERT OR REPLACE INTO users (chat_id, user_data) VALUES (?, ?)", changed)
 
207
  conn.commit()
208
  conn.close()
209
 
210
+ for cid, _ in changed:
211
+ last_saved_state[cid] = copy.deepcopy(db_data[cid])
 
212
  except Exception as e:
213
+ print(f"خطا در ذخیره اعتبار: {e}")
214
 
215
  def is_message_processed(message_id):
 
216
  try:
217
+ conn = sqlite3.connect(DB_FILE, timeout=30.0)
218
  cursor = conn.cursor()
219
  cursor.execute("SELECT 1 FROM processed_messages WHERE message_id = ?", (str(message_id),))
220
+ res = cursor.fetchone()
221
  conn.close()
222
+ return res is not None
223
+ except: return False
 
224
 
225
  def mark_message_processed(message_id):
 
226
  try:
227
+ conn = sqlite3.connect(DB_FILE, timeout=30.0)
228
+ conn.execute("INSERT OR IGNORE INTO processed_messages (message_id) VALUES (?)", (str(message_id),))
 
 
229
  conn.commit()
230
  conn.close()
231
+ except: pass
 
232
 
 
233
  user_credits_db = load_db()
234
 
235