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")))