Add source
This commit is contained in:
161
bot/utils/user_info_updater.py
Normal file
161
bot/utils/user_info_updater.py
Normal 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)
|
||||
|
||||
Reference in New Issue
Block a user