ПОФИКСИТЕ ОТПРАВКУ В WIDGETS.PY

ПРОШУ
This commit is contained in:
kldk-lab 2025-01-26 23:31:15 +03:00 committed by GitHub
parent 21e2069e2f
commit 3611bcde31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 333 additions and 0 deletions

28
src/app.py Normal file
View File

@ -0,0 +1,28 @@
from telethon import TelegramClient, events
from textual.app import App
from tokens import api_id, api_hash
from src.screens import AuthScreen, ChatScreen
class TelegramTUI(App):
"""Класс приложения"""
CSS_PATH = "style.tcss"
async def on_mount(self) -> None:
self.telegram_client = TelegramClient("user", api_id, api_hash)
await self.telegram_client.connect()
chat_screen = ChatScreen(telegram_client=self.telegram_client)
self.install_screen(chat_screen, name="chats")
if not await self.telegram_client.is_user_authorized():
auth_screen = AuthScreen(telegram_client=self.telegram_client)
self.install_screen(auth_screen, name="auth")
self.push_screen("auth")
else:
await self.telegram_client.start()
self.push_screen("chats")
async def on_exit_app(self):
await self.telegram_client.disconnect()
return super()._on_exit_app()

149
src/screens.py Normal file
View File

@ -0,0 +1,149 @@
from textual.screen import Screen
from textual.widgets import Label, Input, Footer, Static
from textual.containers import Vertical, Horizontal, VerticalScroll
from telethon.errors import SessionPasswordNeededError
from telethon import TelegramClient, events, utils
from src.widgets import Dialog, Chat
class AuthScreen(Screen):
"""Класс логина в аккаунт"""
def __init__(
self,
name = None,
id = None,
classes = None,
telegram_client: TelegramClient | None = None
):
super().__init__(name, id, classes)
self.client = telegram_client
def on_mount(self):
self.ac = self.query_one("#auth_container")
def compose(self):
with Vertical(id="auth_container"):
yield Label("Добро пожаловать в Telegram TUI")
yield Input(placeholder="Номер телефона", id="phone")
yield Input(placeholder="Код", id="code", disabled=True)
yield Input(
placeholder="Пароль",
id="password",
password=True,
disabled=True
)
async def on_input_submitted(self, event: Input.Submitted) -> None:
if event.input.id == "phone":
self.phone = event.value
self.ac.query_one("#phone").disabled = True
self.ac.query_one("#code").disabled = False
await self.client.send_code_request(phone=self.phone)
elif event.input.id == "code":
try:
self.code = event.value
self.ac.query_one("#code").disabled = True
await self.client.sign_in(phone=self.phone, code=self.code)
self.app.pop_screen()
self.app.push_screen("chats")
except SessionPasswordNeededError:
self.ac.query_one("#code").disabled = True
self.ac.query_one("#password").disabled = False
elif event.input.id == "password":
self.password = event.value
await self.client.sign_in(password=self.password)
await self.client.start()
self.app.pop_screen()
self.app.push_screen("chats")
self.app.notify("")
class ChatScreen(Screen):
"""Класс экрана чатов, он же основной экран приложения"""
def __init__(
self,
name = None,
id = None,
classes = None,
telegram_client: TelegramClient | None = None
):
super().__init__(name, id, classes)
self.telegram_client = telegram_client
async def on_mount(self):
self.limit = 30
self.chat_container = self\
.query_one("#main_container")\
.query_one("#chats")\
.query_one("#chat_container")
print("Первоначальная загрузка виджетов чатов...")
self.mount_chats(
len(
await self.telegram_client.get_dialogs(
limit=self.limit, archived=False
)
)
)
print("Первоначальная загрузка виджетов чата завершена")
self.is_chat_update_blocked = False
await self.update_chat_list()
print("Первоначальная загрузка чатов завершена")
for event in (
events.NewMessage(),
events.MessageDeleted(),
events.MessageEdited()
):
self.telegram_client.on(event)(self.update_chat_list)
def mount_chats(self, limit: int):
print("Загрузка виджетов чатов...")
chats_amount = len(self.chat_container.query(Chat))
if limit > chats_amount:
for i in range(limit - chats_amount):
chat = Chat(id=f"chat-{i + chats_amount + 1}")
self.chat_container.mount(chat)
elif limit < chats_amount:
for i in range(chats_amount - limit):
self.chat_container.query(Chat).last().remove()
print("Виджеты чатов загружены")
async def update_chat_list(self, event = None):
print("Запрос обновления чатов")
if not self.is_chat_update_blocked:
self.is_chat_update_blocked = True
dialogs = await self.telegram_client.get_dialogs(
limit=self.limit, archived=False
)
print("Получены диалоги")
limit = len(dialogs)
#limit = 30
self.mount_chats(limit)
for i in range(limit):
chat = self.chat_container.query_one(f"#chat-{i + 1}")
chat.username = str(dialogs[i].name)
chat.msg = str(dialogs[i].message.message)
chat.peer_id = dialogs[i].id
#self.notify("Новое сообщение") #колхоз дебаг
self.is_chat_update_blocked = False
print("Чаты обновлены")
else:
print("Обновление чатов невозможно: уже выполняется")
def compose(self):
yield Footer()
with Horizontal(id="main_container"):
with Horizontal(id="chats"):
yield VerticalScroll(Static(id="chat_container"))
#TODO: сделать кнопку чтобы прогрузить больше чатов
yield Dialog(telegram_client=self.telegram_client)

47
src/style.tcss Normal file
View File

@ -0,0 +1,47 @@
#chats {
width: 30%;
}
#dialog {
width: 70%;
}
Chat {
height: 3;
}
Rule {
color: #FFFFFF;
}
.message {
height: 3;
padding: 1;
}
Message {
height: auto;
width: auto;
}
Message Container {
height: auto;
}
#input_place {
height: 3;
width: 70%;
align-horizontal: center;
}
#msg_input {
width: 65%;
}
#send {
}
#auth_container{
align: center middle;
}

109
src/widgets.py Normal file
View File

@ -0,0 +1,109 @@
from textual.containers import Horizontal, Vertical, Container, VerticalScroll
from textual.widget import Widget
from textual.reactive import Reactive
from textual.widgets import Input, Button, Label
from telethon import TelegramClient, events, utils
class Chat(Widget):
"""Класс виджета чата для панели чатов"""
username = Reactive(" ", recompose=True)
msg = Reactive(" ", recompose=True)
peer_id = Reactive(0)
def __init__(
self,
name: str | None = None,
notify_func = None,
id: str | None = None,
classes: str | None = None,
disabled: bool = False
):
super().__init__(
name=str(name),
id=id,
classes=classes,
disabled=disabled
)
self.notify = notify_func
def _on_click(self):
self.msg = str(self.peer_id)
self.notify("нажат чат")
def compose(self):
with Horizontal():
yield Label(f"┌───┐\n{self.username[:1]}\n└───┘")
with Vertical():
yield Label(self.username, id="name")
yield Label(self.msg, id="last_msg")
class Dialog(Widget):
"""Класс окна диалога"""
def __init__(self, id=None, classes=None, disabled=False, telegram_client: TelegramClient | None = None):
super().__init__(id=id, classes=classes, disabled=disabled)
self.telegram_client = telegram_client
def compose(self):
with Vertical():
with VerticalScroll(id="dialog"):
yield Message(message="привет, я ыплыжлп", is_me=True)
yield Message(message="о, дщытрапшщцрущ", is_me=False)
yield Message(message="ДАТОУШЩАРШЩУРЩША!!!!", is_me=False)
# должно быть примерно
# is_me = message.from_id == client.get_peer_id("me")
# но я могу ошибаться, я это фиш если что
#TODO: сделать кнопку чтобы прогрузить больше сообщений,
#но при этом чтобы при перезаходе в чат оставались
#прогруженными только 10 сообщений,
#а остальные декомпоузились
with Horizontal(id="input_place"):
yield Input(placeholder="Сообщение", id="msg_input")
yield Button(label="", id="send", variant="primary")
def on_button_pressed(self, event): # self добавил
self.app.notify("Нажато отправить")
self.message_text = self.query_one("#msg_input").value
self.telegram_client.send_message("ultimate_fish", self.message_text)
class Message(Widget):
"""Класс виджета сообщений для окна диалога"""
def __init__(
self,
name=None,
message=None,
is_me=None,
id=None,
classes=None,
disabled=False
):
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
self.message = message
self.is_me = is_me
def on_mount(self):
container = self.query_one(Container)
label = container.query_one(Label)
if self.is_me:
self.styles.padding = (0, 0, 0, 15)
label.styles.text_align = "right"
container.styles.align_horizontal = "right"
label.styles.border = ("solid", "#4287f5")
else:
self.styles.padding = (0, 15, 0, 0)
label.styles.text_align = "left"
container.styles.align_horizontal = "left"
label.styles.border = ("solid", "#ffffff")
def compose(self):
with Container():
yield Label(str(self.message))