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

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +92 -47
main.py CHANGED
@@ -92,118 +92,163 @@ def gregorian_to_jalali(gy, gm, gd):
92
  return jy, jm, jd
93
 
94
  # ==============================================================================
95
- # 🟢 پارت 4: دیتابیس SQLite (نسخه ضد خرابی و خود-تعمیرگر)
96
  # ==============================================================================
 
 
 
 
 
 
97
  DB_FILE = "/data/users_db.db"
98
  OLD_JSON_FILE = "/data/users_db.json"
 
 
99
  last_saved_state = {}
100
 
101
  def init_sqlite_db():
 
102
  try:
 
103
  os.makedirs(os.path.dirname(DB_FILE), exist_ok=True)
104
- conn = sqlite3.connect(DB_FILE, timeout=30.0) # افزایش تایم‌اوت برای جلوگیری از قفل شدن
 
105
  cursor = conn.cursor()
106
 
107
- # ۱. بررسی سلامت دیتابیس و تعمیر خودکار در صورت خرابی (Malformed)
108
- try:
109
- cursor.execute('PRAGMA integrity_check;')
110
- result = cursor.fetchone()
111
- if result[0] != "ok":
112
- print("⚠️ دیتابیس دچار خرابی شده، در حال تعمیر...")
113
- cursor.execute('PRAGMA journal_mode=DELETE;') # موقتاً تغییر حالت برای تعمیر
114
- cursor.execute('VACUUM;') # بازسازی کامل فایل دیتابیس
115
- print("✅ دیتابیس با موفقیت بازسازی و تعمیر شد.")
116
- except Exception as e:
117
- print(f"🛠 تلاش برای تعمیر اضطراری: {e}")
118
-
119
- # ۲. فعال‌سازی حالت بهینه
120
  cursor.execute('PRAGMA journal_mode=WAL;')
121
- cursor.execute('PRAGMA synchronous=NORMAL;') # امنیت بیشتر برای جلوگیری از مالفورم شدن
122
-
123
- cursor.execute('CREATE TABLE IF NOT EXISTS users (chat_id TEXT PRIMARY KEY, user_data TEXT)')
124
- cursor.execute('CREATE TABLE IF NOT EXISTS processed_messages (message_id TEXT PRIMARY KEY)')
 
 
 
 
 
 
 
 
 
 
 
125
 
126
  conn.commit()
127
  conn.close()
128
  except Exception as e:
129
- print("خطا در راه‌اندازی دیتابیس:", e)
130
 
131
  def migrate_old_json_to_sqlite():
 
132
  if os.path.exists(OLD_JSON_FILE):
133
- print("⚠️ انتقال اطلاعات از JSON قدیمی...")
134
  try:
135
  with open(OLD_JSON_FILE, "r", encoding="utf-8") as f:
136
  old_data = json.load(f)
 
137
  if old_data:
138
  conn = sqlite3.connect(DB_FILE)
 
 
 
139
  for chat_id, data in old_data.items():
140
  user_data_str = json.dumps(data, ensure_ascii=False)
141
- conn.execute("INSERT OR IGNORE INTO users (chat_id, user_data) VALUES (?, ?)", (str(chat_id), user_data_str))
 
142
  conn.commit()
143
  conn.close()
144
- os.rename(OLD_JSON_FILE, OLD_JSON_FILE + ".bak")
145
- print("✅ انتقال تمام شد.")
 
 
 
 
146
  except Exception as e:
147
- print(f"❌ خطا در مهاجرت: {e}")
148
 
149
  def load_db():
 
150
  global last_saved_state
 
 
151
  init_sqlite_db()
 
 
152
  migrate_old_json_to_sqlite()
 
153
  db_dict = {}
154
  try:
155
  conn = sqlite3.connect(DB_FILE)
156
  cursor = conn.cursor()
157
  cursor.execute("SELECT chat_id, user_data FROM users")
158
- for row in cursor.fetchall():
159
- db_dict[row[0]] = json.loads(row[1])
 
 
160
  conn.close()
 
 
161
  last_saved_state = copy.deepcopy(db_dict)
162
- print(f"✅ دیتابیس لود شد. کاربران: {len(db_dict)}")
163
  return db_dict
164
  except Exception as e:
165
- print(f"⚠️ خطا در لود دیتابیس: {e}")
166
  return {}
167
 
168
  def save_db(db_data):
 
169
  global last_saved_state
170
  try:
171
- changed = []
172
- for cid, data in db_data.items():
173
- if cid not in last_saved_state or last_saved_state[cid] != data:
174
- changed.append((str(cid), json.dumps(data, ensure_ascii=False)))
175
 
176
- if not changed: return
 
 
 
 
 
 
 
177
 
178
- conn = sqlite3.connect(DB_FILE, timeout=30.0)
179
- conn.execute('PRAGMA journal_mode=WAL;')
180
- conn.executemany("INSERT OR REPLACE INTO users (chat_id, user_data) VALUES (?, ?)", changed)
 
 
181
  conn.commit()
182
  conn.close()
183
 
184
- for cid, _ in changed:
185
- last_saved_state[cid] = copy.deepcopy(db_data[cid])
 
186
  except Exception as e:
187
- print("خطا در ذخیره SQLite:", e)
188
 
189
  def is_message_processed(message_id):
 
190
  try:
191
- conn = sqlite3.connect(DB_FILE, timeout=20.0)
192
  cursor = conn.cursor()
193
  cursor.execute("SELECT 1 FROM processed_messages WHERE message_id = ?", (str(message_id),))
194
- res = cursor.fetchone()
195
  conn.close()
196
- return res is not None
197
- except: return False
 
198
 
199
  def mark_message_processed(message_id):
 
200
  try:
201
- conn = sqlite3.connect(DB_FILE, timeout=20.0)
202
- conn.execute("INSERT OR IGNORE INTO processed_messages (message_id) VALUES (?)", (str(message_id),))
 
 
203
  conn.commit()
204
  conn.close()
205
- except: pass
 
206
 
 
207
  user_credits_db = load_db()
208
 
209
 
 
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