410 lines
11 KiB
Python
410 lines
11 KiB
Python
"""
|
||
Утилиты для работы с информацией о пользователях
|
||
"""
|
||
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)
|
||
}
|