Add source

This commit is contained in:
2025-12-04 00:12:56 +03:00
parent b75875df5e
commit 0cb7045e7a
75 changed files with 9055 additions and 0 deletions

View File

@@ -0,0 +1,161 @@
"""
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)