Управление информацией пользователей
This commit is contained in:
409
bot/utils/usernames.py
Normal file
409
bot/utils/usernames.py
Normal file
@@ -0,0 +1,409 @@
|
||||
"""
|
||||
Утилиты для работы с информацией о пользователях
|
||||
"""
|
||||
from typing import Optional, Union
|
||||
from enum import Enum
|
||||
|
||||
from aiogram.types import Message, CallbackQuery, User, InlineQuery, ChatMemberUpdated
|
||||
|
||||
__all__ = (
|
||||
'get_user_display_name',
|
||||
'get_user_mention',
|
||||
'get_user_id',
|
||||
'username',
|
||||
'format_user',
|
||||
'UserFormat',
|
||||
'is_bot',
|
||||
'has_username',
|
||||
'is_premium',
|
||||
'get_language_code',
|
||||
'compare_users',
|
||||
'get_user_info_dict'
|
||||
)
|
||||
|
||||
|
||||
class UserFormat(str, Enum):
|
||||
"""Форматы отображения пользователя"""
|
||||
USERNAME = 'username' # @username или @id123
|
||||
FULL_NAME = 'full_name' # Имя Фамилия
|
||||
MENTION = 'mention' # HTML mention
|
||||
MENTION_MARKDOWN = 'markdown' # Markdown mention
|
||||
FIRST_NAME = 'first_name' # Только имя
|
||||
ID_ONLY = 'id' # Только ID
|
||||
DETAILED = 'detailed' # @username (Имя Фамилия, ID: 123)
|
||||
|
||||
|
||||
# Тип для всех событий с пользователем
|
||||
EventType = Union[Message, CallbackQuery, InlineQuery, ChatMemberUpdated]
|
||||
|
||||
|
||||
def _extract_user(event: EventType) -> Optional[User]:
|
||||
"""
|
||||
Извлекает объект User из события.
|
||||
|
||||
Args:
|
||||
event: Объект события
|
||||
|
||||
Returns:
|
||||
User или None
|
||||
"""
|
||||
if isinstance(event, (Message, CallbackQuery, InlineQuery)):
|
||||
return event.from_user
|
||||
elif isinstance(event, ChatMemberUpdated):
|
||||
return event.from_user or event.new_chat_member.user
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_user_display_name(
|
||||
event: EventType,
|
||||
default: str = "Unknown User"
|
||||
) -> str:
|
||||
"""
|
||||
Возвращает отображаемое имя пользователя (Full Name).
|
||||
|
||||
Args:
|
||||
event: Объект события (Message, CallbackQuery, и т.д.)
|
||||
default: Значение по умолчанию если пользователь не найден
|
||||
|
||||
Returns:
|
||||
str: Полное имя пользователя
|
||||
|
||||
Example:
|
||||
>> get_user_display_name(message)
|
||||
'John Doe'
|
||||
>> get_user_display_name(message)
|
||||
'John' # Если нет фамилии
|
||||
"""
|
||||
user = _extract_user(event)
|
||||
|
||||
if not user:
|
||||
return default
|
||||
|
||||
# Полное имя (приоритет)
|
||||
if user.full_name:
|
||||
return user.full_name
|
||||
|
||||
# Только имя
|
||||
if user.first_name:
|
||||
return user.first_name
|
||||
|
||||
# Username как запасной вариант
|
||||
if user.username:
|
||||
return f"@{user.username}"
|
||||
|
||||
# ID как последний вариант
|
||||
return f"User {user.id}"
|
||||
|
||||
|
||||
def get_user_mention(
|
||||
event: EventType,
|
||||
parse_mode: str = 'HTML',
|
||||
show_username: bool = False
|
||||
) -> str:
|
||||
"""
|
||||
Возвращает упоминание пользователя (кликабельное).
|
||||
|
||||
Args:
|
||||
event: Объект события
|
||||
parse_mode: Режим парсинга ('HTML' или 'Markdown')
|
||||
show_username: Показывать username вместо имени
|
||||
|
||||
Returns:
|
||||
str: HTML/Markdown упоминание
|
||||
|
||||
Example:
|
||||
>> get_user_mention(message)
|
||||
'<a href="tg://user?id=123456789">John Doe</a>'
|
||||
|
||||
>> get_user_mention(message, parse_mode='Markdown')
|
||||
'[John Doe](tg://user?id=123456789)'
|
||||
|
||||
>> get_user_mention(message, show_username=True)
|
||||
'<a href="tg://user?id=123456789">@johndoe</a>'
|
||||
"""
|
||||
user = _extract_user(event)
|
||||
|
||||
if not user:
|
||||
return "Unknown User"
|
||||
|
||||
# Определяем текст для отображения
|
||||
if show_username and user.username:
|
||||
display_text = f"@{user.username}"
|
||||
else:
|
||||
display_text = user.full_name or user.first_name or f"User {user.id}"
|
||||
|
||||
# Формируем ссылку
|
||||
user_link = f"tg://user?id={user.id}"
|
||||
|
||||
if parse_mode.upper() == 'HTML':
|
||||
return f'<a href="{user_link}">{display_text}</a>'
|
||||
elif parse_mode.upper() in ('MARKDOWN', 'MARKDOWNV2'):
|
||||
# Экранируем специальные символы для Markdown
|
||||
display_text = display_text.replace('[', '\\[').replace(']', '\\]')
|
||||
return f'[{display_text}]({user_link})'
|
||||
else:
|
||||
return display_text
|
||||
|
||||
|
||||
def get_user_id(event: EventType) -> Optional[int]:
|
||||
"""
|
||||
Возвращает ID пользователя.
|
||||
|
||||
Args:
|
||||
event: Объект события
|
||||
|
||||
Returns:
|
||||
int или None: ID пользователя
|
||||
|
||||
Example:
|
||||
>> get_user_id(message)
|
||||
123456789
|
||||
"""
|
||||
user = _extract_user(event)
|
||||
return user.id if user else None
|
||||
|
||||
|
||||
def username(
|
||||
event: EventType,
|
||||
with_at: bool = True,
|
||||
fallback_to_id: bool = True
|
||||
) -> str:
|
||||
"""
|
||||
Возвращает username пользователя или ID если username отсутствует.
|
||||
|
||||
Это основная функция для получения идентификатора пользователя
|
||||
в формате @username или @id123.
|
||||
|
||||
Args:
|
||||
event: Объект события (Message, CallbackQuery, и т.д.)
|
||||
with_at: Добавлять @ в начало
|
||||
fallback_to_id: Использовать ID если нет username
|
||||
|
||||
Returns:
|
||||
str: Username или ID пользователя
|
||||
|
||||
Raises:
|
||||
ValueError: Если информация о пользователе отсутствует
|
||||
|
||||
Example:
|
||||
>> username(message)
|
||||
'@johndoe'
|
||||
|
||||
>> username(message) # Нет username
|
||||
'@123456789'
|
||||
|
||||
>> username(message, with_at=False)
|
||||
'johndoe'
|
||||
|
||||
>> username(message, fallback_to_id=False)
|
||||
'' # Если нет username
|
||||
"""
|
||||
user = _extract_user(event)
|
||||
|
||||
if not user:
|
||||
raise ValueError("Информация о пользователе отсутствует в событии")
|
||||
|
||||
# Если есть username
|
||||
if user.username:
|
||||
return f"@{user.username}" if with_at else user.username
|
||||
|
||||
# Fallback на ID
|
||||
if fallback_to_id:
|
||||
return f"@{user.id}" if with_at else str(user.id)
|
||||
|
||||
# Если ничего нет
|
||||
return ""
|
||||
|
||||
|
||||
def format_user(
|
||||
event: EventType,
|
||||
format_type: UserFormat = UserFormat.USERNAME,
|
||||
default: str = "@System"
|
||||
) -> str:
|
||||
"""
|
||||
Универсальная функция форматирования пользователя.
|
||||
|
||||
Args:
|
||||
event: Объект события
|
||||
format_type: Тип форматирования (из enum UserFormat)
|
||||
default: Значение по умолчанию
|
||||
|
||||
Returns:
|
||||
str: Отформатированная информация о пользователе
|
||||
|
||||
Example:
|
||||
>> format_user(message, UserFormat.USERNAME)
|
||||
'@johndoe'
|
||||
|
||||
>> format_user(message, UserFormat.FULL_NAME)
|
||||
'John Doe'
|
||||
|
||||
>> format_user(message, UserFormat.MENTION)
|
||||
'<a href="tg://user?id=123">John Doe</a>'
|
||||
|
||||
>> format_user(message, UserFormat.DETAILED)
|
||||
'@johndoe (John Doe, ID: 123456789)'
|
||||
"""
|
||||
user = _extract_user(event)
|
||||
|
||||
if not user:
|
||||
return default
|
||||
|
||||
# USERNAME: @username или @id
|
||||
if format_type == UserFormat.USERNAME:
|
||||
if user.username:
|
||||
return f"@{user.username}"
|
||||
return f"@{user.id}"
|
||||
|
||||
# FULL_NAME: Имя Фамилия
|
||||
elif format_type == UserFormat.FULL_NAME:
|
||||
return user.full_name or user.first_name or f"User {user.id}"
|
||||
|
||||
# MENTION: HTML упоминание
|
||||
elif format_type == UserFormat.MENTION:
|
||||
display = user.full_name or user.first_name or f"User {user.id}"
|
||||
return f'<a href="tg://user?id={user.id}">{display}</a>'
|
||||
|
||||
# MENTION_MARKDOWN: Markdown упоминание
|
||||
elif format_type == UserFormat.MENTION_MARKDOWN:
|
||||
display = user.full_name or user.first_name or f"User {user.id}"
|
||||
display = display.replace('[', '\\[').replace(']', '\\]')
|
||||
return f'[{display}](tg://user?id={user.id})'
|
||||
|
||||
# FIRST_NAME: Только имя
|
||||
elif format_type == UserFormat.FIRST_NAME:
|
||||
return user.first_name or f"User {user.id}"
|
||||
|
||||
# ID_ONLY: Только ID
|
||||
elif format_type == UserFormat.ID_ONLY:
|
||||
return str(user.id)
|
||||
|
||||
# DETAILED: Подробная информация
|
||||
elif format_type == UserFormat.DETAILED:
|
||||
parts = []
|
||||
|
||||
# Username
|
||||
if user.username:
|
||||
parts.append(f"@{user.username}")
|
||||
|
||||
# Full name
|
||||
if user.full_name:
|
||||
parts.append(f"({user.full_name}")
|
||||
elif user.first_name:
|
||||
parts.append(f"({user.first_name}")
|
||||
|
||||
# ID
|
||||
parts.append(f"ID: {user.id})")
|
||||
|
||||
return ' '.join(parts) if parts else f"User {user.id}"
|
||||
|
||||
# По умолчанию
|
||||
return default
|
||||
|
||||
|
||||
# ================= ДОПОЛНИТЕЛЬНЫЕ УТИЛИТЫ =================
|
||||
|
||||
def is_bot(event: EventType) -> bool:
|
||||
"""
|
||||
Проверяет, является ли пользователь ботом.
|
||||
|
||||
Args:
|
||||
event: Объект события
|
||||
|
||||
Returns:
|
||||
bool: True если бот
|
||||
"""
|
||||
user = _extract_user(event)
|
||||
return user.is_bot if user else False
|
||||
|
||||
|
||||
def has_username(event: EventType) -> bool:
|
||||
"""
|
||||
Проверяет, есть ли у пользователя username.
|
||||
|
||||
Args:
|
||||
event: Объект события
|
||||
|
||||
Returns:
|
||||
bool: True если есть username
|
||||
"""
|
||||
user = _extract_user(event)
|
||||
return bool(user and user.username)
|
||||
|
||||
|
||||
def is_premium(event: EventType) -> bool:
|
||||
"""
|
||||
Проверяет, есть ли у пользователя Telegram Premium.
|
||||
|
||||
Args:
|
||||
event: Объект события
|
||||
|
||||
Returns:
|
||||
bool: True если Premium
|
||||
"""
|
||||
user = _extract_user(event)
|
||||
return user.is_premium if user else False
|
||||
|
||||
|
||||
def get_language_code(event: EventType) -> Optional[str]:
|
||||
"""
|
||||
Возвращает код языка пользователя.
|
||||
|
||||
Args:
|
||||
event: Объект события
|
||||
|
||||
Returns:
|
||||
Optional[str]: Код языка ('ru', 'en', и т.д.)
|
||||
"""
|
||||
user = _extract_user(event)
|
||||
return user.language_code if user else None
|
||||
|
||||
|
||||
def compare_users(event1: EventType, event2: EventType) -> bool:
|
||||
"""
|
||||
Сравнивает двух пользователей по ID.
|
||||
|
||||
Args:
|
||||
event1: Первое событие
|
||||
event2: Второе событие
|
||||
|
||||
Returns:
|
||||
bool: True если это один и тот же пользователь
|
||||
"""
|
||||
user1 = _extract_user(event1)
|
||||
user2 = _extract_user(event2)
|
||||
|
||||
if not user1 or not user2:
|
||||
return False
|
||||
|
||||
return user1.id == user2.id
|
||||
|
||||
|
||||
def get_user_info_dict(event: EventType) -> dict:
|
||||
"""
|
||||
Возвращает всю информацию о пользователе в виде словаря.
|
||||
|
||||
Args:
|
||||
event: Объект события
|
||||
|
||||
Returns:
|
||||
dict: Словарь с информацией о пользователе
|
||||
"""
|
||||
user = _extract_user(event)
|
||||
|
||||
if not user:
|
||||
return {}
|
||||
|
||||
return {
|
||||
'id': user.id,
|
||||
'username': user.username,
|
||||
'first_name': user.first_name,
|
||||
'last_name': user.last_name,
|
||||
'full_name': user.full_name,
|
||||
'is_bot': user.is_bot,
|
||||
'is_premium': user.is_premium,
|
||||
'language_code': user.language_code,
|
||||
'mention': get_user_mention(event),
|
||||
'display_name': get_user_display_name(event)
|
||||
}
|
||||
Reference in New Issue
Block a user