всё фиговенько, но не криминал

This commit is contained in:
kirill 2025-02-10 22:21:40 +03:00
parent 828adaf91a
commit 8c69a666bb
4 changed files with 118 additions and 68 deletions

View File

@ -11,7 +11,7 @@ class TelegramTUI(App):
CSS_PATH = "style.tcss" CSS_PATH = "style.tcss"
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("user2", api_id, api_hash)
await self.telegram_client.connect() await self.telegram_client.connect()
chat_screen = ChatScreen(telegram_client=self.telegram_client) chat_screen = ChatScreen(telegram_client=self.telegram_client)

View File

@ -1,7 +1,7 @@
"""Файл с кастомными экранами приложения""" """Файл с кастомными экранами приложения"""
from textual.screen import Screen from textual.screen import Screen
from textual.widgets import Label, Input, Footer, Static from textual.widgets import Label, Input, Footer, Static, ContentSwitcher
from textual.containers import Vertical, Horizontal, VerticalScroll from textual.containers import Vertical, Horizontal, VerticalScroll
from telethon.errors import SessionPasswordNeededError from telethon.errors import SessionPasswordNeededError
from telethon import TelegramClient, events from telethon import TelegramClient, events
@ -72,7 +72,7 @@ class ChatScreen(Screen):
self.telegram_client = telegram_client self.telegram_client = telegram_client
async def on_mount(self): async def on_mount(self):
self.limit = 30 self.limit = 100
self.chat_container = self\ self.chat_container = self\
.query_one("#main_container")\ .query_one("#main_container")\
@ -147,4 +147,5 @@ class ChatScreen(Screen):
with Horizontal(id="chats"): with Horizontal(id="chats"):
yield VerticalScroll(id="chat_container") yield VerticalScroll(id="chat_container")
#TODO: сделать кнопку чтобы прогрузить больше чатов #TODO: сделать кнопку чтобы прогрузить больше чатов
yield Dialog(telegram_client=self.telegram_client) yield ContentSwitcher(id="dialog_switcher")
#yield Dialog(telegram_client=self.telegram_client)

View File

@ -14,11 +14,6 @@ Rule {
color: #FFFFFF; color: #FFFFFF;
} }
.message {
height: 3;
padding: 1;
}
Message { Message {
height: auto; height: auto;
width: auto; width: auto;
@ -28,11 +23,6 @@ Message Container {
height: auto; height: auto;
} }
Message Static {
height: auto;
width: auto;
}
#input_place { #input_place {
height: 3; height: 3;
width: 70%; width: 70%;
@ -46,3 +36,29 @@ Message Static {
#auth_container{ #auth_container{
align: center middle; align: center middle;
} }
.is_me_true Container {
padding: 0 0 0 15;
align: right middle;
}
.is_me_true Static {
border: solid $primary;
width: auto;
height: auto;
text-align: right;
min-width: 11;
}
.is_me_false Container {
padding: 0 15 0 0;
align: left middle;
}
.is_me_false Static {
border: solid $foreground;
width: auto;
height: auto;
text-align: left;
min-width: 11;
}

View File

@ -3,15 +3,17 @@
from textual.containers import Horizontal, Vertical, Container, VerticalScroll from textual.containers import Horizontal, Vertical, Container, VerticalScroll
from textual.widget import Widget from textual.widget import Widget
from textual.reactive import Reactive from textual.reactive import Reactive
from textual.widgets import Input, Button, Label, Static from textual.widgets import Input, Button, Label, Static, ContentSwitcher
from telethon import TelegramClient, events from textual.app import ComposeResult, RenderResult
from telethon import TelegramClient, events, utils
import datetime
class Chat(Widget): class Chat(Widget):
"""Класс виджета чата для панели чатов""" """Класс виджета чата для панели чатов"""
username = Reactive(" ", recompose=True) username: Reactive[str] = Reactive(" ", recompose=True)
msg = Reactive(" ", recompose=True) msg: Reactive[str] = Reactive(" ", recompose=True)
peer_id = Reactive(0) peer_id: Reactive[int] = Reactive(0)
def __init__( def __init__(
self, self,
@ -19,19 +21,35 @@ class Chat(Widget):
id: str | None = None, id: str | None = None,
classes: str | None = None, classes: str | None = None,
disabled: bool = False disabled: bool = False
): ) -> None:
super().__init__( super().__init__(
name=str(name), name=str(name),
id=id, id=id,
classes=classes, classes=classes,
disabled=disabled disabled=disabled
) )
def on_mount(self) -> None:
self.switcher = self.screen.query_one(Horizontal).query_one("#dialog_switcher", ContentSwitcher)
def _on_click(self): def on_click(self) -> None:
self.msg = str(self.peer_id) dialog_id = f"dialog-{str(self.peer_id)}"
self.app.notify("нажат чат") print("click 1")
try:
self.switcher.mount(Dialog(
telegram_client=self.app.telegram_client,
chat_id=self.peer_id,
id=dialog_id
))
print("click 1.1")
except:
print("click 1.2")
print("click 2")
self.switcher.current = dialog_id
self.switcher.recompose()
print("click 3")
def compose(self): def compose(self) -> ComposeResult:
with Horizontal(): with Horizontal():
yield Label(f"┌───┐\n{self.username[:1]}\n└───┘") yield Label(f"┌───┐\n{self.username[:1]}\n└───┘")
with Vertical(): with Vertical():
@ -46,21 +64,23 @@ class Dialog(Widget):
id=None, id=None,
classes=None, classes=None,
disabled=None, disabled=None,
telegram_client: TelegramClient | None = None telegram_client: TelegramClient | None = None,
): chat_id = None
) -> None:
super().__init__(id=id, classes=classes, disabled=disabled) super().__init__(id=id, classes=classes, disabled=disabled)
self.telegram_client = telegram_client self.telegram_client = telegram_client
self.chat_id = -1002299818671 self.chat_id = chat_id
self.is_msg_update_blocked = False self.is_msg_update_blocked = False
async def on_mount(self): async def on_mount(self) -> None:
self.limit = 30 self.limit = 10
self.msg_input = self.query_one("#msg_input") self.msg_input = self.query_one("#msg_input")
self.dialog = self.query_one(Vertical).query_one("#dialog") self.dialog = self.query_one(Vertical).query_one("#dialog")
self.me = await self.telegram_client.get_me() self.me = await self.telegram_client.get_me()
self.dialog.scroll_end(animate=False)
await self.update_dialog() await self.update_dialog()
for event in ( for event in (
@ -68,22 +88,26 @@ class Dialog(Widget):
events.MessageDeleted, events.MessageDeleted,
events.MessageEdited events.MessageEdited
): ):
self.telegram_client.on(event(chats=(self.chat_id)))\ self.telegram_client.on(
(self.update_dialog) event(chats=(self.chat_id))
)(self.update_dialog)
def mount_messages(self, limit: int): def mount_messages(self, limit: int) -> None:
print("Загрузка виджетов сообщений...") print("Загрузка виджетов сообщений...")
msg_amount = len(self.dialog.query(Message)) msg_amount = len(self.dialog.query(Message))
if limit > msg_amount: if limit > msg_amount:
for i in range(limit - msg_amount): for i in range(limit - msg_amount):
self.dialog.mount(Message(id=f"msg-{i + msg_amount + 1}")) self.dialog.mount(
Message(id=f"msg-{i + msg_amount + 1}"),
before=0
)
elif limit < msg_amount: elif limit < msg_amount:
for i in range(msg_amount - limit): for i in range(msg_amount - limit):
self.dialog.query(Message).last().remove() self.dialog.query(Message).last().remove()
async def update_dialog(self, event = None): async def update_dialog(self, event = None) -> None:
print("Запрос обновления сообщений") print("Запрос обновления сообщений")
if not self.is_msg_update_blocked: if not self.is_msg_update_blocked:
@ -98,70 +122,79 @@ class Dialog(Widget):
self.mount_messages(limit) self.mount_messages(limit)
for i in range(limit): for i in range(limit):
chat = self.dialog.query_one(f"#msg-{i + 1}") msg = self.dialog.query_one(f"#msg-{i + 1}")
chat.message = str(messages[i].message) + \ msg.message = ""
(not str(messages[i].message)) * " " if str(messages[i].message):
chat.is_me = messages[i].from_id == self.me.id msg.message = str(messages[i].message)
chat._update_styles()
#TODO: завести это:
is_me = messages[i].from_id.user_id == self.me.id
msg.is_me = is_me
msg.username = utils.get_display_name(messages[i].sender)
msg.send_time = messages[i]\
.date\
.astimezone(datetime.timezone.utc)\
.strftime("%H:%M")
self.is_msg_update_blocked = False self.is_msg_update_blocked = False
print("Сообщения обновлены") print("Сообщения обновлены")
else: else:
print("Обновление сообщений невозможно: уже выполняется") print("Обновление сообщений невозможно: уже выполняется")
def compose(self): def compose(self) -> ComposeResult:
with Vertical(): with Vertical():
yield VerticalScroll(id="dialog") yield VerticalScroll(id="dialog")
with Horizontal(id="input_place"): with Horizontal(id="input_place"):
yield Input(placeholder="Сообщение", id="msg_input") yield Input(placeholder="Сообщение", id="msg_input")
yield Button(label="", id="send", variant="primary") yield Button(label="", id="send", variant="primary")
async def on_button_pressed(self, event = None): async def on_button_pressed(self, event = None) -> None:
await self.send_message() await self.send_message()
async def on_input_submitted(self, event = None): async def on_input_submitted(self, event = None) -> None:
await self.send_message() await self.send_message()
async def send_message(self): async def send_message(self) -> None:
await self.telegram_client.send_message( try:
self.chat_id, await self.telegram_client.send_message(
str(self.msg_input.value) self.chat_id,
) str(self.msg_input.value)
)
except ValueError:
self.app.notify("Ошибка отправки")
self.msg_input.value = "" self.msg_input.value = ""
await self.update_dialog() await self.update_dialog()
class Message(Widget): class Message(Widget):
"""Класс виджета сообщений для окна диалога""" """Класс виджета сообщений для окна диалога"""
message = Reactive("", recompose=True) message: Reactive[str] = Reactive("", recompose=True)
is_me = Reactive(False, recompose=True) is_me: Reactive[bool] = Reactive(False, recompose=True)
username: Reactive[str] = Reactive("", recompose=True)
send_time: Reactive[str] = Reactive("", recompose=True)
def __init__( def __init__(
self, self,
name=None, name=None,
message=None,
is_me=None,
id=None, id=None,
classes=None, classes=None,
disabled=False disabled=False
): ) -> None:
super().__init__(name=name, id=id, classes=classes, disabled=disabled) super().__init__(name=name, id=id, classes=classes, disabled=disabled)
self.message = message
self.is_me = is_me
def on_mount(self): def on_mount(self) -> None:
container = self.query_one(Container) pass
label_border = container.query_one(".border")
if self.is_me:
self.styles.padding = (0, 0, 0, 15)
container.styles.align_horizontal = "right"
label_border.styles.border = ("solid", "#4287f5")
else:
self.styles.padding = (0, 15, 0, 0)
container.styles.align_horizontal = "left"
label_border.styles.border = ("solid", "#ffffff")
def compose(self): def compose(self) -> ComposeResult:
static = Static(self.message)
static.border_title = self.username * (not self.is_me)
static.border_subtitle = self.send_time
with Container(): with Container():
with Static(classes="border"): yield static
yield Static(str(self.message))
if self.is_me:
self.classes = "is_me_true"
else:
self.classes = "is_me_false"