bot/models/user.py
2026-08-30 13:04:57 +00:00

162 lines
6.2 KiB
Python

import json
import os
from typing import Any, Iterator
from telebot.types import CallbackQuery, InlineQuery, Message
from models import logger
from .schedule import (
Schedule,
ScheduleAPIError,
ScheduleTarget,
get_schedule,
group_target,
normalize_target,
target_key,
)
class User:
def __init__(self, userID: int) -> None:
self.id = userID
try:
with open(f"./users/{self.id}.json", "r", encoding="utf-8") as file:
self.data: dict[str, Any] = json.load(file)
except FileNotFoundError:
self.data = {}
except (json.JSONDecodeError, OSError):
logger.error("Bot", f'INVALID "{userID}" SAVED DATA! RESET!')
self.data = {}
self.state: str | bool | None = self.data.get("state")
legacy_group = self.data.get("group")
self.active_target = normalize_target(self.data.get("active_schedule"))
if self.active_target is None and isinstance(legacy_group, str) and legacy_group:
self.active_target = group_target(legacy_group)
saved = [normalize_target(value) for value in self.data.get("saved_schedules", [])]
self.saved_schedules: list[ScheduleTarget] = [value for value in saved if value]
for name in self.data.get("saved_groups", []):
if isinstance(name, str) and name:
self._append_saved(group_target(name))
if self.active_target:
self._append_saved(self.active_target)
self.view_target = normalize_target(self.data.get("view_schedule"))
self.search_kind = self.data.get("search_kind")
results = [normalize_target(value) for value in self.data.get("search_results", [])]
self.search_results: list[ScheduleTarget] = [value for value in results if value]
self.notifications: list[str] = self.data.get("notifications", [])
self.valid = bool(self.data.get("valid", True))
self.user: dict[str, Any] = self.data.get("user", {})
self.usage: dict[str, Any] = self.data.get(
"usage", {"count": -1, "date": None, "time": None}
)
self.flags: list[str] = self.data.get("flags", [])
if self.data.get("notifyEvening") and "evening" not in self.notifications:
self.notifications.append("evening")
if self.data.get("notifyBeforeLesson") and "lesson" not in self.notifications:
self.notifications.append("lesson")
self.save()
@property
def group(self) -> str | None:
return self.active_target["name"] if self.active_target else None
@group.setter
def group(self, name: str | None) -> None:
self.active_target = group_target(name) if name else None
@property
def saved_groups(self) -> list[str]:
return [target["name"] for target in self.saved_schedules if target["kind"] == "group"]
@saved_groups.setter
def saved_groups(self, names: list[str]) -> None:
non_groups = [target for target in self.saved_schedules if target["kind"] != "group"]
self.saved_schedules = non_groups + [group_target(name) for name in names if name]
def _append_saved(self, target: ScheduleTarget) -> None:
if not any(target_key(value) == target_key(target) for value in self.saved_schedules):
self.saved_schedules.append(target)
def is_saved(self, target: ScheduleTarget | None) -> bool:
return bool(
target
and any(target_key(value) == target_key(target) for value in self.saved_schedules)
)
def save_schedule(self, target: ScheduleTarget, make_active: bool = True) -> None:
self._append_saved(target)
if make_active:
self.active_target = target
self.save()
def remove_schedule(self, target: ScheduleTarget) -> None:
key = target_key(target)
self.saved_schedules = [value for value in self.saved_schedules if target_key(value) != key]
if self.active_target and target_key(self.active_target) == key:
self.active_target = self.saved_schedules[-1] if self.saved_schedules else None
self.save()
def keep_only_active(self) -> None:
self.saved_schedules = [self.active_target] if self.active_target else []
self.save()
def find_saved(self, name: str) -> ScheduleTarget | None:
return next((target for target in self.saved_schedules if target["name"] == name), None)
def schedule(self, target: ScheduleTarget | None = None) -> Schedule | None:
selected = target or self.active_target
if not selected:
return None
try:
return get_schedule(selected)
except (ValueError, ScheduleAPIError) as error:
logger.error("Schedule", f"[{self.id}] {error}")
return None
def setState(self, newState: str) -> None:
self.state = newState
self.save()
def save(self) -> None:
with open(f"./users/{self.id}.json", "w", encoding="utf-8") as file:
json.dump(
{
"state": self.state,
"group": self.group,
"active_schedule": self.active_target,
"saved_groups": self.saved_groups,
"saved_schedules": self.saved_schedules,
"view_schedule": self.view_target,
"search_kind": self.search_kind,
"search_results": self.search_results,
"notifications": self.notifications,
"valid": self.valid,
"user": self.user,
"usage": self.usage,
"flags": self.flags,
},
file,
ensure_ascii=False,
indent=4,
)
def validate(ctx: Message | InlineQuery | CallbackQuery) -> User:
try:
return User(ctx.from_user.id) # type: ignore
except Exception as error:
logger.error("Bot", f'INVALID "{ctx.from_user.id}" VALIDATION! {error}')
return False # type: ignore
def getAllUsers() -> Iterator[User]:
for _, _, filenames in os.walk("./users"):
for userFile in filenames:
if userFile.endswith(".json"):
yield User(int(userFile.removesuffix(".json")))