162 lines
5.7 KiB
Python
162 lines
5.7 KiB
Python
"""
|
|
Utilities for updating user information from Telegram API
|
|
"""
|
|
import asyncio
|
|
import logging
|
|
from typing import Optional, Dict
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select
|
|
from shared.database.session import get_async_session_local
|
|
from shared.database.models import User
|
|
from bot.utils.telegram_user import get_user_info
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Constants for time intervals
|
|
SECONDS_PER_HOUR = 3600
|
|
SECONDS_PER_MINUTE = 60
|
|
TELEGRAM_API_DELAY = 0.5 # Delay between Telegram API requests in seconds
|
|
USER_INFO_UPDATE_INTERVAL_HOURS = 24 # Interval for updating user information
|
|
ERROR_RETRY_DELAY_SECONDS = 60 # Delay before retry on error
|
|
|
|
|
|
def _update_user_fields(user: User, user_info: Dict[str, Optional[str]]) -> bool:
|
|
"""
|
|
Update user fields from Telegram API information.
|
|
|
|
Updates username, first_name, and last_name if they are missing
|
|
and available in user_info.
|
|
|
|
Args:
|
|
user: User object from database
|
|
user_info: Dictionary with user information from Telegram API
|
|
|
|
Returns:
|
|
True if any fields were updated, False otherwise
|
|
"""
|
|
updated = False
|
|
|
|
if not user.username and user_info.get("username"):
|
|
user.username = user_info.get("username")
|
|
updated = True
|
|
|
|
if not user.first_name and user_info.get("first_name"):
|
|
user.first_name = user_info.get("first_name")
|
|
updated = True
|
|
|
|
if not user.last_name and user_info.get("last_name"):
|
|
user.last_name = user_info.get("last_name")
|
|
updated = True
|
|
|
|
return updated
|
|
|
|
|
|
async def update_user_info_from_telegram(
|
|
user_id: int,
|
|
db_session: Optional[AsyncSession] = None
|
|
) -> bool:
|
|
"""
|
|
Update user information from Telegram API.
|
|
|
|
Fetches user information from Telegram API and updates the database
|
|
with missing fields (username, first_name, last_name).
|
|
|
|
Args:
|
|
user_id: Telegram user ID
|
|
db_session: Database session (if None, creates a new one)
|
|
|
|
Returns:
|
|
True if information was updated, False otherwise
|
|
"""
|
|
try:
|
|
# Get user information from Telegram API
|
|
user_info = await get_user_info(user_id)
|
|
if not user_info:
|
|
return False
|
|
|
|
# Update information in database
|
|
if db_session:
|
|
# Use provided session
|
|
user = await db_session.get(User, user_id)
|
|
if user:
|
|
if _update_user_fields(user, user_info):
|
|
await db_session.commit()
|
|
logger.info(f"User {user_id} information updated from Telegram API")
|
|
return True
|
|
else:
|
|
# Create new session
|
|
async with get_async_session_local()() as session:
|
|
user = await session.get(User, user_id)
|
|
if user:
|
|
if _update_user_fields(user, user_info):
|
|
await session.commit()
|
|
logger.info(f"User {user_id} information updated from Telegram API")
|
|
return True
|
|
|
|
return False
|
|
except Exception as e:
|
|
logger.error(f"Error updating user {user_id} information: {e}", exc_info=True)
|
|
return False
|
|
|
|
|
|
async def update_users_without_info_periodically(
|
|
interval_hours: int = USER_INFO_UPDATE_INTERVAL_HOURS
|
|
) -> None:
|
|
"""
|
|
Periodically update information for users without username or first_name.
|
|
|
|
Runs in an infinite loop, updating user information at specified intervals.
|
|
Can be cancelled with asyncio.CancelledError.
|
|
|
|
Args:
|
|
interval_hours: Interval between updates in hours (default: 24 hours)
|
|
"""
|
|
logger.info("Background task for updating user information started")
|
|
|
|
while True:
|
|
try:
|
|
await asyncio.sleep(interval_hours * SECONDS_PER_HOUR)
|
|
|
|
logger.info("Starting update of user information for users without username or first_name")
|
|
|
|
async with get_async_session_local()() as session:
|
|
# Get users without username or first_name
|
|
result = await session.execute(
|
|
select(User).where(
|
|
(User.username == None) | (User.first_name == None)
|
|
)
|
|
)
|
|
users = result.scalars().all()
|
|
|
|
updated_count = 0
|
|
error_count = 0
|
|
|
|
for user in users:
|
|
try:
|
|
# Update user information
|
|
if await update_user_info_from_telegram(user.user_id, db_session=session):
|
|
updated_count += 1
|
|
|
|
# Delay between requests to avoid overloading Telegram API
|
|
await asyncio.sleep(TELEGRAM_API_DELAY)
|
|
except Exception as e:
|
|
error_count += 1
|
|
logger.warning(f"Error updating user {user.user_id}: {e}")
|
|
|
|
if updated_count > 0 or error_count > 0:
|
|
logger.info(
|
|
f"User information update completed: "
|
|
f"updated {updated_count}, errors {error_count}, total checked {len(users)}"
|
|
)
|
|
else:
|
|
logger.debug("No users found for update")
|
|
|
|
except asyncio.CancelledError:
|
|
logger.info("User information update task stopped")
|
|
break
|
|
except Exception as e:
|
|
logger.error(f"Error in user information update task: {e}", exc_info=True)
|
|
# Continue working even on error
|
|
await asyncio.sleep(ERROR_RETRY_DELAY_SECONDS)
|
|
|