Files
muha-bot/database.py
T
2026-07-09 23:34:34 +03:00

501 lines
8.8 KiB
Python

import aiosqlite
from datetime import datetime
import json
DB_NAME = "database/database.db"
async def init_db():
async with aiosqlite.connect(DB_NAME) as db:
# Пользователи
await db.execute("""
CREATE TABLE IF NOT EXISTS users(
user_id INTEGER PRIMARY KEY,
username TEXT,
first_name TEXT,
last_name TEXT,
phone TEXT,
email TEXT,
group_id INTEGER,
group_name TEXT,
subgroup TEXT,
created_at TEXT,
last_seen TEXT
)
""")
# Кэш расписания
await db.execute("""
CREATE TABLE IF NOT EXISTS schedule_cache(
id INTEGER PRIMARY KEY AUTOINCREMENT,
group_id INTEGER NOT NULL,
date TEXT NOT NULL,
data TEXT NOT NULL,
created_at TEXT NOT NULL,
UNIQUE(group_id, date)
)
""")
# Индекс
await db.execute("""
CREATE INDEX IF NOT EXISTS idx_schedule_cache
ON schedule_cache(group_id, date)
""")
# Поддержка
await db.execute("""
CREATE TABLE IF NOT EXISTS support(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
message TEXT NOT NULL,
answer TEXT,
status TEXT DEFAULT 'open',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
await db.commit()
async def save_group(
user_id: int,
group_id: int,
group_name: str,
subgroup: str | None = None
):
async with aiosqlite.connect(DB_NAME) as db:
await db.execute(
"""
INSERT INTO users(
user_id,
group_id,
group_name,
subgroup
)
VALUES(?,?,?,?)
ON CONFLICT(user_id)
DO UPDATE SET
group_id = excluded.group_id,
group_name = excluded.group_name,
subgroup = excluded.subgroup
""",
(
user_id,
group_id,
group_name,
subgroup
)
)
await db.commit()
async def get_group(
user_id: int
):
async with aiosqlite.connect(DB_NAME) as db:
cursor = await db.execute(
"""
SELECT
group_id,
group_name,
subgroup
FROM users
WHERE user_id=?
""",
(
user_id,
)
)
row = await cursor.fetchone()
if row is None:
return None
return {
"group_id": row[0],
"group_name": row[1],
"subgroup": row[2]
}
async def delete_group(
user_id: int
):
async with aiosqlite.connect(DB_NAME) as db:
await db.execute(
"""
DELETE FROM users
WHERE user_id=?
""",
(
user_id,
)
)
await db.commit()
async def update_subgroup(
user_id: int,
subgroup: str
):
async with aiosqlite.connect(DB_NAME) as db:
await db.execute(
"""
UPDATE users
SET subgroup=?
WHERE user_id=?
""",
(
subgroup,
user_id
)
)
await db.commit()
async def create_ticket(
user_id: int,
message: str
):
async with aiosqlite.connect(DB_NAME) as db:
cursor = await db.execute(
"""
INSERT INTO support(
user_id,
message
)
VALUES(?,?)
""",
(
user_id,
message
)
)
await db.commit()
return cursor.lastrowid
#tickets
async def get_ticket(ticket_id: int):
async with aiosqlite.connect(DB_NAME) as db:
cursor = await db.execute(
"""
SELECT
id,
user_id,
message,
status
FROM support
WHERE id=?
""",
(ticket_id,)
)
row = await cursor.fetchone()
return row
async def get_tickets():
async with aiosqlite.connect(DB_NAME) as db:
cursor = await db.execute(
"""
SELECT
id,
user_id,
message,
status
FROM support
WHERE status='open'
ORDER BY id DESC
"""
)
rows = await cursor.fetchall()
return rows
async def close_ticket(ticket_id: int):
async with aiosqlite.connect(DB_NAME) as db:
await db.execute(
"""
UPDATE support
SET status='closed'
WHERE id=?
""",
(ticket_id,)
)
await db.commit()
async def update_user(
user_id: int,
username: str | None = None,
first_name: str | None = None,
last_name: str | None = None
):
now = datetime.now().isoformat()
async with aiosqlite.connect(DB_NAME) as db:
await db.execute(
"""
INSERT INTO users(
user_id,
username,
first_name,
last_name,
created_at,
last_seen
)
VALUES(?,?,?,?,?,?)
ON CONFLICT(user_id)
DO UPDATE SET
username=excluded.username,
first_name=excluded.first_name,
last_name=excluded.last_name,
last_seen=excluded.last_seen
""",
(
user_id,
username,
first_name,
last_name,
now,
now
)
)
await db.commit()
async def save_phone(
user_id: int,
phone: str
):
async with aiosqlite.connect(DB_NAME) as db:
await db.execute(
"""
UPDATE users
SET phone=?
WHERE user_id=?
""",
(
phone,
user_id
)
)
await db.commit()
async def get_cached_schedule(
group_id: int,
schedule_date: str,
ttl_minutes: int
):
async with aiosqlite.connect(DB_NAME) as db:
cursor = await db.execute(
"""
SELECT
data,
created_at
FROM schedule_cache
WHERE group_id=?
AND date=?
""",
(
group_id,
schedule_date
)
)
row = await cursor.fetchone()
if row is None:
return None
created = datetime.fromisoformat(
row[1]
)
age = (
datetime.now() - created
).total_seconds() / 60
# кэш устарел
if age > ttl_minutes:
return None
return json.loads(row[0])
async def save_schedule_cache(
group_id: int,
schedule_date: str,
data: dict
):
async with aiosqlite.connect(DB_NAME) as db:
await db.execute(
"""
INSERT INTO schedule_cache(
group_id,
date,
data,
created_at
)
VALUES(?,?,?,?)
ON CONFLICT(group_id, date)
DO UPDATE SET
data = excluded.data,
created_at = excluded.created_at
""",
(
group_id,
schedule_date,
json.dumps(
data,
ensure_ascii=False
),
datetime.now().isoformat()
)
)
await db.commit()
async def clear_old_cache(days: int = 30):
async with aiosqlite.connect(DB_NAME) as db:
cursor = await db.execute(
"""
DELETE FROM schedule_cache
WHERE datetime(created_at)
< datetime('now', ?)
""",
(f"-{days} days",)
)
await db.commit()
return cursor.rowcount
async def get_active_groups():
async with aiosqlite.connect(DB_NAME) as db:
cursor = await db.execute(
"""
SELECT DISTINCT group_id
FROM users
WHERE group_id IS NOT NULL
"""
)
rows = await cursor.fetchall()
return [row[0] for row in rows]