Что-то поменял обратно, тем не менее, спасибо контрибьюторам

This commit is contained in:
kirill 2025-03-29 01:58:39 +03:00
parent c01d5954dc
commit e523fce7bd
5 changed files with 74 additions and 43 deletions

2
.gitignore vendored
View File

@ -5,3 +5,5 @@ __pycache__
*/__pycache__ */__pycache__
tokens.py tokens.py
.env .env
.venv
.python-version

View File

@ -38,5 +38,5 @@ cp .env.example .env
## Запуск ## Запуск
```bash ```bash
python src/app.py ./main.py
``` ```

View File

@ -9,8 +9,11 @@ from rich.console import Console
from src.screens import AuthScreen, ChatScreen from src.screens import AuthScreen, ChatScreen
# Настройка консоли для корректной работы с Unicode # Настройка консоли для корректной работы с Unicode
"""
console = Console(force_terminal=True, color_system="auto") console = Console(force_terminal=True, color_system="auto")
sys.stdout = console sys.stdout = console
"""
# спойлер: не помогло
load_dotenv() load_dotenv()
@ -33,7 +36,6 @@ class TelegramTUI(App):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.console = console
async def on_mount(self) -> None: async def on_mount(self) -> None:
self.telegram_client = TelegramClient("user", api_id, api_hash) self.telegram_client = TelegramClient("user", api_id, api_hash)
@ -52,3 +54,6 @@ class TelegramTUI(App):
async def on_exit_app(self): async def on_exit_app(self):
await self.telegram_client.disconnect() await self.telegram_client.disconnect()
return super()._on_exit_app() return super()._on_exit_app()
if __name__ == "__main__":
raise Exception("Запущен не тот файл. Запустите main.py.")

View File

@ -8,6 +8,7 @@ from telethon.errors import SessionPasswordNeededError
from telethon import TelegramClient, events from telethon import TelegramClient, events
from src.widgets import Dialog, Chat, normalize_text from src.widgets import Dialog, Chat, normalize_text
from textual import log from textual import log
from textual.keys import Keys, _character_to_key
class AuthScreen(Screen): class AuthScreen(Screen):
"""Класс экрана логина в аккаунт""" """Класс экрана логина в аккаунт"""
@ -63,6 +64,13 @@ class AuthScreen(Screen):
class ChatScreen(Screen): class ChatScreen(Screen):
"""Класс экрана чатов, он же основной экран приложения""" """Класс экрана чатов, он же основной экран приложения"""
BINDINGS = [
(Keys.Tab, "log(\"Нажат таб\")", "Переключение фокуса"),
(Keys.Enter, "log(\"Нажат энтер\")", "Открыть"),
(Keys.Escape, "log(\"Нажат эскейп\")", "Назад"),
(_character_to_key("/"), "log(\"Нажат слэш\")", "Поиск")
]
def __init__( def __init__(
self, self,
name = None, name = None,
@ -86,7 +94,6 @@ class ChatScreen(Screen):
.query_one("#chat_container") .query_one("#chat_container")
self.search_input = self.query_one("#search_input") self.search_input = self.query_one("#search_input")
self.help_label = self.query_one("#help_label")
log("Первоначальная загрузка виджетов чатов...") log("Первоначальная загрузка виджетов чатов...")
self.mount_chats( self.mount_chats(
@ -140,7 +147,8 @@ class ChatScreen(Screen):
if self.search_query: if self.search_query:
dialogs = [ dialogs = [
d for d in dialogs d for d in dialogs
if self.search_query.lower() in normalize_text(d.name).lower() if self.search_query.lower() in \
normalize_text(d.name).lower()
] ]
limit = len(dialogs) limit = len(dialogs)
@ -152,7 +160,8 @@ class ChatScreen(Screen):
chat.msg = normalize_text(str(dialogs[i].message.message)) chat.msg = normalize_text(str(dialogs[i].message.message))
chat.peer_id = dialogs[i].id chat.peer_id = dialogs[i].id
chat.is_selected = (i == self.selected_chat_index) chat.is_selected = (i == self.selected_chat_index)
chat.is_focused = (self.focused_element == "chat_list" and i == self.selected_chat_index) chat.is_focused = (self.focused_element == "chat_list" and \
i == self.selected_chat_index)
self.is_chat_update_blocked = False self.is_chat_update_blocked = False
log("Чаты обновлены") log("Чаты обновлены")
@ -166,7 +175,7 @@ class ChatScreen(Screen):
self.update_chat_list() self.update_chat_list()
def on_key(self, event: Key) -> None: def on_key(self, event: Key) -> None:
if event.key == "tab": if event.key == Keys.Tab:
# Переключаем фокус между элементами # Переключаем фокус между элементами
if self.focused_element == "search": if self.focused_element == "search":
self.focused_element = "chat_list" self.focused_element = "chat_list"
@ -185,7 +194,8 @@ class ChatScreen(Screen):
if not chats: if not chats:
return return
if event.key == "up": match event.key:
case Keys.Up:
self.selected_chat_index = max(0, self.selected_chat_index - 1) self.selected_chat_index = max(0, self.selected_chat_index - 1)
for i, chat in enumerate(chats): for i, chat in enumerate(chats):
chat.is_selected = (i == self.selected_chat_index) chat.is_selected = (i == self.selected_chat_index)
@ -193,7 +203,7 @@ class ChatScreen(Screen):
# Прокручиваем к выбранному чату # Прокручиваем к выбранному чату
selected_chat = chats[self.selected_chat_index] selected_chat = chats[self.selected_chat_index]
self.chat_container.scroll_to(selected_chat, animate=False) self.chat_container.scroll_to(selected_chat, animate=False)
elif event.key == "down": case Keys.Down:
self.selected_chat_index = min(len(chats) - 1, self.selected_chat_index + 1) self.selected_chat_index = min(len(chats) - 1, self.selected_chat_index + 1)
for i, chat in enumerate(chats): for i, chat in enumerate(chats):
chat.is_selected = (i == self.selected_chat_index) chat.is_selected = (i == self.selected_chat_index)
@ -201,13 +211,13 @@ class ChatScreen(Screen):
# Прокручиваем к выбранному чату # Прокручиваем к выбранному чату
selected_chat = chats[self.selected_chat_index] selected_chat = chats[self.selected_chat_index]
self.chat_container.scroll_to(selected_chat, animate=False) self.chat_container.scroll_to(selected_chat, animate=False)
elif event.key == "enter": case Keys.Enter:
chats[self.selected_chat_index].on_click() chats[self.selected_chat_index].on_click()
elif event.key == "escape": case Keys.Escape:
# Возвращаемся к списку чатов # Возвращаемся к списку чатов
self.app.pop_screen() self.app.pop_screen()
self.app.push_screen("chats") self.app.push_screen("chats")
elif event.key == "/": case "/": #Не работает: нужен кейкод слэша
# Фокус на поиск # Фокус на поиск
self.focused_element = "search" self.focused_element = "search"
self.search_input.focus() self.search_input.focus()
@ -217,12 +227,10 @@ class ChatScreen(Screen):
yield Footer() yield Footer()
with Horizontal(id="main_container"): with Horizontal(id="main_container"):
with Vertical(id="chats"): with Vertical(id="chats"):
yield Label(
"Навигация: Tab - переключение фокуса, ↑↓ - выбор чата, Enter - открыть, Esc - назад, / - поиск",
id="help_label",
classes="help-text"
)
yield Input(placeholder=normalize_text("Поиск чатов..."), id="search_input") yield Input(placeholder=normalize_text("Поиск чатов..."), id="search_input")
yield VerticalScroll(id="chat_container") yield VerticalScroll(id="chat_container")
yield ContentSwitcher(id="dialog_switcher") yield ContentSwitcher(id="dialog_switcher")
#yield Dialog(telegram_client=self.telegram_client) #yield Dialog(telegram_client=self.telegram_client)
if __name__ == "__main__":
raise Exception("Запущен не тот файл. Запустите main.py.")

View File

@ -13,8 +13,9 @@ import emoji
import os import os
import tempfile import tempfile
from PIL import Image from PIL import Image
import pywhatkit as kit #import pywhatkit as kit
from textual import log from textual import log
from warnings import deprecated
def remove_emoji(text: str) -> str: def remove_emoji(text: str) -> str:
"""Удаляет эмодзи из текста""" """Удаляет эмодзи из текста"""
@ -29,11 +30,12 @@ def normalize_text(text: str) -> str:
# Удаляем эмодзи # Удаляем эмодзи
text = remove_emoji(text) text = remove_emoji(text)
# Удаляем все управляющие символы # Удаляем все управляющие символы
text = ''.join(char for char in text if unicodedata.category(char)[0] != 'C') text = ''\
.join(char for char in text if unicodedata.category(char)[0] != 'C')
# Нормализуем Unicode # Нормализуем Unicode
text = unicodedata.normalize('NFKC', text) text = unicodedata.normalize('NFKC', text)
# Заменяем специальные символы на их ASCII-эквиваленты # Заменяем специальные символы на их ASCII-эквиваленты
text = text.replace('', '-').replace('', '-').replace('', '...') text = text.replace('', '-').replace('', '-')
# Удаляем все непечатаемые символы # Удаляем все непечатаемые символы
text = ''.join(char for char in text if char.isprintable()) text = ''.join(char for char in text if char.isprintable())
return text return text
@ -47,6 +49,7 @@ def safe_ascii(text: str) -> str:
# Оставляем только ASCII символы и пробелы # Оставляем только ASCII символы и пробелы
return ''.join(char for char in text if ord(char) < 128 or char.isspace()) return ''.join(char for char in text if ord(char) < 128 or char.isspace())
@deprecated("Не работает на моём компьютере.")
def convert_image_to_ascii(image_path: str, width: int = 50) -> str: def convert_image_to_ascii(image_path: str, width: int = 50) -> str:
"""Конвертирует изображение в ASCII-арт""" """Конвертирует изображение в ASCII-арт"""
try: try:
@ -123,11 +126,21 @@ class Chat(Widget):
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
with Horizontal(classes="chat-item"): with Horizontal(classes="chat-item"):
"""
# Используем ASCII-символы для рамки # Используем ASCII-символы для рамки
yield Label(f"+---+\n| {safe_ascii(self.username[:1].upper()):1} |\n+---+") yield Label(
f"┌───┐\n{normalize_text(
self.username[:1].upper()
):1} \n"
)
with Vertical(): with Vertical():
yield Label(normalize_text(self.username), id="name") yield Label(normalize_text(self.username), id="name")
yield Label(normalize_text(self.msg), id="last_msg") yield Label(normalize_text(self.msg), id="last_msg")
"""
yield Label(f"┌───┐\n{self.username[:1].upper():1}\n└───┘")
with Vertical():
yield Label(self.username, id="name")
yield Label(self.msg, id="last_msg")
def on_mouse_enter(self) -> None: def on_mouse_enter(self) -> None:
self.add_class("hover") self.add_class("hover")
@ -324,3 +337,6 @@ class Message(Widget):
self.classes = "is_me_true" self.classes = "is_me_true"
else: else:
self.classes = "is_me_false" self.classes = "is_me_false"
if __name__ == "__main__":
raise Exception("Запущен не тот файл. Запустите main.py.")