Files
PrimoGuardBot-/bot/utils/usernames.py
2026-02-17 11:24:55 +07:00

410 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Утилиты для работы с информацией о пользователях
"""
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)
}