Spaces:
Sleeping
Sleeping
File size: 9,020 Bytes
831b557 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | """
الذاكرة الدائمة - SQLite
يحفظ:
- سياق كل مستخدم (آخر N رسالة)
- تفضيلات المستخدم (النموذج المفضل، اللغة)
- إحصائيات الاستخدام
"""
import asyncio
import json
import logging
import os
import time
from typing import List, Dict, Optional
import aiosqlite
from config import config
logger = logging.getLogger(__name__)
class Memory:
"""إدارة ذاكرة البوت الدائمة عبر SQLite"""
def __init__(self, db_path: str):
self.db_path = db_path
# تأكد من وجود المجلد الأب
os.makedirs(os.path.dirname(self.db_path) or ".", exist_ok=True)
self._lock = asyncio.Lock()
async def init(self):
"""تهيئة قاعدة البيانات"""
async with aiosqlite.connect(self.db_path) as db:
await db.execute("""
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
role TEXT NOT NULL,
content TEXT NOT NULL,
model TEXT,
timestamp INTEGER NOT NULL
)
""")
await db.execute("""
CREATE INDEX IF NOT EXISTS idx_messages_user
ON messages(user_id, timestamp DESC)
""")
await db.execute("""
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY,
username TEXT,
first_name TEXT,
language TEXT DEFAULT 'ar',
preferred_model TEXT,
is_owner INTEGER DEFAULT 0,
is_admin INTEGER DEFAULT 0,
first_seen INTEGER NOT NULL,
last_seen INTEGER NOT NULL,
message_count INTEGER DEFAULT 0
)
""")
await db.execute("""
CREATE TABLE IF NOT EXISTS github_cache (
user_id INTEGER NOT NULL,
repo_full_name TEXT NOT NULL,
data TEXT NOT NULL,
timestamp INTEGER NOT NULL,
PRIMARY KEY (user_id, repo_full_name)
)
""")
await db.commit()
logger.info(f"Database initialized at {self.db_path}")
async def register_user(
self,
user_id: int,
username: str = "",
first_name: str = "",
) -> Dict:
"""تسجيل أو تحديث مستخدم"""
now = int(time.time())
is_owner = 1 if config.is_owner(user_id) else 0
is_admin = 1 if config.is_admin(user_id) else 0
async with self._lock, aiosqlite.connect(self.db_path) as db:
# تحقق من وجود المستخدم
cursor = await db.execute(
"SELECT user_id, message_count FROM users WHERE user_id = ?",
(user_id,),
)
row = await cursor.fetchone()
if row is None:
await db.execute(
"""INSERT INTO users
(user_id, username, first_name, language, is_owner, is_admin,
first_seen, last_seen, message_count)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0)""",
(user_id, username, first_name, "ar", is_owner, is_admin, now, now),
)
await db.commit()
return {
"user_id": user_id, "username": username,
"first_name": first_name, "is_new": True,
"message_count": 0,
}
else:
await db.execute(
"""UPDATE users SET
username = ?, first_name = ?, is_owner = ?, is_admin = ?,
last_seen = ?
WHERE user_id = ?""",
(username, first_name, is_owner, is_admin, now, user_id),
)
await db.commit()
return {
"user_id": user_id, "username": username,
"first_name": first_name, "is_new": False,
"message_count": row[1],
}
async def increment_message_count(self, user_id: int):
async with self._lock, aiosqlite.connect(self.db_path) as db:
await db.execute(
"UPDATE users SET message_count = message_count + 1 WHERE user_id = ?",
(user_id,),
)
await db.commit()
async def add_message(
self,
user_id: int,
role: str,
content: str,
model: str = "",
):
"""إضافة رسالة للذاكرة"""
# استخدام time.time() بدلاً من int(time.time()) لدقة أعلى
now = time.time()
async with self._lock, aiosqlite.connect(self.db_path) as db:
await db.execute(
"""INSERT INTO messages (user_id, role, content, model, timestamp)
VALUES (?, ?, ?, ?, ?)""",
(user_id, role, content, model, now),
)
# حذف الرسائل القديمة الزائدة عن الحد
await db.execute(
"""DELETE FROM messages WHERE user_id = ? AND id NOT IN (
SELECT id FROM messages WHERE user_id = ?
ORDER BY timestamp DESC, id DESC LIMIT ?
)""",
(user_id, user_id, config.MAX_HISTORY_PER_USER),
)
await db.commit()
async def get_history(self, user_id: int) -> List[Dict[str, str]]:
"""جلب سياق محادثة المستخدم (مرتب زمنياً، مع id كحاكم)"""
async with aiosqlite.connect(self.db_path) as db:
cursor = await db.execute(
"""SELECT role, content FROM messages
WHERE user_id = ? AND role IN ('user', 'assistant')
ORDER BY timestamp ASC, id ASC""",
(user_id,),
)
rows = await cursor.fetchall()
return [{"role": r[0], "content": r[1]} for r in rows]
async def clear_history(self, user_id: int) -> int:
"""حذف ذاكرة مستخدم - يعيد عدد الرسائل المحذوفة"""
async with self._lock, aiosqlite.connect(self.db_path) as db:
cursor = await db.execute(
"SELECT COUNT(*) FROM messages WHERE user_id = ?",
(user_id,),
)
count = (await cursor.fetchone())[0]
await db.execute("DELETE FROM messages WHERE user_id = ?", (user_id,))
await db.commit()
return count
async def set_preferred_model(self, user_id: int, model: str):
async with self._lock, aiosqlite.connect(self.db_path) as db:
await db.execute(
"UPDATE users SET preferred_model = ? WHERE user_id = ?",
(model, user_id),
)
await db.commit()
async def get_preferred_model(self, user_id: int) -> Optional[str]:
async with aiosqlite.connect(self.db_path) as db:
cursor = await db.execute(
"SELECT preferred_model FROM users WHERE user_id = ?",
(user_id,),
)
row = await cursor.fetchone()
return row[0] if row and row[0] else None
async def get_user_language(self, user_id: int) -> str:
async with aiosqlite.connect(self.db_path) as db:
cursor = await db.execute(
"SELECT language FROM users WHERE user_id = ?",
(user_id,),
)
row = await cursor.fetchone()
return row[0] if row else "ar"
async def set_user_language(self, user_id: int, lang: str):
async with self._lock, aiosqlite.connect(self.db_path) as db:
await db.execute(
"UPDATE users SET language = ? WHERE user_id = ?",
(lang, user_id),
)
await db.commit()
async def get_stats(self) -> Dict:
"""إحصائيات عامة"""
async with aiosqlite.connect(self.db_path) as db:
cursor = await db.execute("SELECT COUNT(*) FROM users")
users_count = (await cursor.fetchone())[0]
cursor = await db.execute("SELECT COUNT(*) FROM messages")
messages_count = (await cursor.fetchone())[0]
cursor = await db.execute(
"SELECT COUNT(*) FROM users WHERE is_owner = 1"
)
owners = (await cursor.fetchone())[0]
return {
"users": users_count,
"messages": messages_count,
"owners_configured": owners,
}
# Singleton
memory = Memory(config.DB_PATH)
|