refactor: streamline configuration loading and enhance admin checks
All checks were successful
CI / lint-and-test (push) Successful in 20s

- Refactored the configuration loading in `config.py` to utilize a single source of truth through the `Settings` class, improving maintainability and clarity.
- Introduced the `is_admin_for_telegram_user` function in `repository.py` to centralize admin checks based on usernames and phone numbers.
- Updated command handlers to use the new admin check function, ensuring consistent access control across the application.
- Enhanced error handling in the `error_handler` to log exceptions when sending error replies to users, improving debugging capabilities.
- Improved the handling of user phone updates in `repository.py` to ensure proper normalization and validation of phone numbers.
This commit is contained in:
2026-02-20 16:42:41 +03:00
parent 9486f7004d
commit ae21883e1e
9 changed files with 79 additions and 50 deletions

View File

@@ -1,12 +1,12 @@
"""Repository: get_or_create_user, get_duties, insert_duty, get_current_duty, group_duty_pins."""
import hashlib
import hmac
import secrets
from datetime import datetime, timedelta, timezone
from sqlalchemy.orm import Session
import duty_teller.config as config
from duty_teller.db.models import User, Duty, GroupDutyPin, CalendarSubscriptionToken
@@ -23,6 +23,22 @@ def get_user_by_telegram_id(session: Session, telegram_user_id: int) -> User | N
return session.query(User).filter(User.telegram_user_id == telegram_user_id).first()
def is_admin_for_telegram_user(session: Session, telegram_user_id: int) -> bool:
"""Check if the Telegram user is admin (by username or by stored phone).
Args:
session: DB session.
telegram_user_id: Telegram user id.
Returns:
True if user is in ADMIN_USERNAMES or their stored phone is in ADMIN_PHONES.
"""
user = get_user_by_telegram_id(session, telegram_user_id)
if not user:
return False
return config.is_admin(user.username or "") or config.is_admin_by_phone(user.phone)
def get_or_create_user(
session: Session,
telegram_user_id: int,
@@ -279,9 +295,6 @@ def get_user_by_calendar_token(session: Session, token: str) -> User | None:
)
if row is None:
return None
# Constant-time compare to avoid timing leaks (token_hash is already hashed).
if not hmac.compare_digest(row[0].token_hash, token_hash_val):
return None
return row[1]
@@ -466,7 +479,10 @@ def set_user_phone(
user = session.query(User).filter(User.telegram_user_id == telegram_user_id).first()
if not user:
return None
user.phone = phone
if phone is None or (isinstance(phone, str) and not phone.strip()):
user.phone = None
else:
user.phone = config.normalize_phone(phone)
session.commit()
session.refresh(user)
return user