Ойойой как сыро, но теперь есть функционал сообщений йоу

This commit is contained in:
fish-dd 2025-01-31 20:46:23 +03:00 committed by GitHub
commit 2602fd2680
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 119 additions and 47 deletions

View File

@ -1,3 +1,5 @@
"""Файл инициализации приложения"""
from src.app import TelegramTUI from src.app import TelegramTUI
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,3 +1,5 @@
"""Главный файл приложения"""
from telethon import TelegramClient, events from telethon import TelegramClient, events
from textual.app import App from textual.app import App
from tokens import api_id, api_hash from tokens import api_id, api_hash

View File

@ -1,12 +1,14 @@
"""Файл с кастомными экранами приложения"""
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
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, utils from telethon import TelegramClient, events
from src.widgets import Dialog, Chat from src.widgets import Dialog, Chat
class AuthScreen(Screen): class AuthScreen(Screen):
"""Класс логина в аккаунт""" """Класс экрана логина в аккаунт"""
def __init__( def __init__(
self, self,
@ -14,7 +16,7 @@ class AuthScreen(Screen):
id = None, id = None,
classes = None, classes = None,
telegram_client: TelegramClient | None = None telegram_client: TelegramClient | None = None
): ):
super().__init__(name, id, classes) super().__init__(name, id, classes)
self.client = telegram_client self.client = telegram_client
@ -55,7 +57,6 @@ class AuthScreen(Screen):
await self.client.start() await self.client.start()
self.app.pop_screen() self.app.pop_screen()
self.app.push_screen("chats") self.app.push_screen("chats")
self.app.notify("")
class ChatScreen(Screen): class ChatScreen(Screen):
"""Класс экрана чатов, он же основной экран приложения""" """Класс экрана чатов, он же основной экран приложения"""
@ -94,11 +95,11 @@ class ChatScreen(Screen):
print("Первоначальная загрузка чатов завершена") print("Первоначальная загрузка чатов завершена")
for event in ( for event in (
events.NewMessage(), events.NewMessage,
events.MessageDeleted(), events.MessageDeleted,
events.MessageEdited() events.MessageEdited
): ):
self.telegram_client.on(event)(self.update_chat_list) self.telegram_client.on(event())(self.update_chat_list)
def mount_chats(self, limit: int): def mount_chats(self, limit: int):
print("Загрузка виджетов чатов...") print("Загрузка виджетов чатов...")
@ -117,14 +118,16 @@ class ChatScreen(Screen):
async def update_chat_list(self, event = None): async def update_chat_list(self, event = None):
print("Запрос обновления чатов") print("Запрос обновления чатов")
if not self.is_chat_update_blocked: if not self.is_chat_update_blocked:
self.is_chat_update_blocked = True self.is_chat_update_blocked = True
dialogs = await self.telegram_client.get_dialogs( dialogs = await self.telegram_client.get_dialogs(
limit=self.limit, archived=False limit=self.limit, archived=False
) )
print("Получены диалоги") print("Получены диалоги")
limit = len(dialogs) limit = len(dialogs)
#limit = 30
self.mount_chats(limit) self.mount_chats(limit)
for i in range(limit): for i in range(limit):
@ -132,7 +135,6 @@ class ChatScreen(Screen):
chat.username = str(dialogs[i].name) chat.username = str(dialogs[i].name)
chat.msg = str(dialogs[i].message.message) chat.msg = str(dialogs[i].message.message)
chat.peer_id = dialogs[i].id chat.peer_id = dialogs[i].id
#self.notify("Новое сообщение") #колхоз дебаг
self.is_chat_update_blocked = False self.is_chat_update_blocked = False
print("Чаты обновлены") print("Чаты обновлены")
@ -143,7 +145,6 @@ class ChatScreen(Screen):
yield Footer() yield Footer()
with Horizontal(id="main_container"): with Horizontal(id="main_container"):
with Horizontal(id="chats"): with Horizontal(id="chats"):
yield VerticalScroll(Static(id="chat_container")) yield VerticalScroll(id="chat_container")
#TODO: сделать кнопку чтобы прогрузить больше чатов #TODO: сделать кнопку чтобы прогрузить больше чатов
yield Dialog(telegram_client=self.telegram_client)
yield Dialog()

View File

@ -28,6 +28,11 @@ Message Container {
height: auto; height: auto;
} }
Message Static {
height: auto;
width: auto;
}
#input_place { #input_place {
height: 3; height: 3;
width: 70%; width: 70%;
@ -38,10 +43,6 @@ Message Container {
width: 65%; width: 65%;
} }
#send {
}
#auth_container{ #auth_container{
align: center middle; align: center middle;
} }

View File

@ -1,7 +1,10 @@
"""Файл с кастомными виджетами приложения"""
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 from textual.widgets import Input, Button, Label, Static
from telethon import TelegramClient, events
class Chat(Widget): class Chat(Widget):
"""Класс виджета чата для панели чатов""" """Класс виджета чата для панели чатов"""
@ -13,22 +16,20 @@ class Chat(Widget):
def __init__( def __init__(
self, self,
name: str | None = None, name: str | None = None,
notify_func = None,
id: str | None = None, id: str | None = None,
classes: str | None = None, classes: str | None = None,
disabled: bool = False disabled: bool = False
): ):
super().__init__( super().__init__(
name=str(name), name=str(name),
id=id, id=id,
classes=classes, classes=classes,
disabled=disabled disabled=disabled
) )
self.notify = notify_func
def _on_click(self): def _on_click(self):
self.msg = str(self.peer_id) self.msg = str(self.peer_id)
self.notify("нажат чат") self.app.notify("нажат чат")
def compose(self): def compose(self):
with Horizontal(): with Horizontal():
@ -40,34 +41,100 @@ class Chat(Widget):
class Dialog(Widget): class Dialog(Widget):
"""Класс окна диалога""" """Класс окна диалога"""
def __init__(self, id=None, classes=None, disabled=False): def __init__(
self,
id=None,
classes=None,
disabled=None,
telegram_client: TelegramClient | None = None
):
super().__init__(id=id, classes=classes, disabled=disabled) super().__init__(id=id, classes=classes, disabled=disabled)
self.telegram_client = telegram_client
self.chat_id = -1002299818671
self.is_msg_update_blocked = False
async def on_mount(self):
self.limit = 30
self.msg_input = self.query_one("#msg_input")
self.dialog = self.query_one(Vertical).query_one("#dialog")
self.me = await self.telegram_client.get_me()
await self.update_dialog()
for event in (
events.NewMessage,
events.MessageDeleted,
events.MessageEdited
):
self.telegram_client.on(event(chats=(self.chat_id)))\
(self.update_dialog)
def mount_messages(self, limit: int):
print("Загрузка виджетов сообщений...")
msg_amount = len(self.dialog.query(Message))
if limit > msg_amount:
for i in range(limit - msg_amount):
self.dialog.mount(Message(id=f"msg-{i + msg_amount + 1}"))
elif limit < msg_amount:
for i in range(msg_amount - limit):
self.dialog.query(Message).last().remove()
async def update_dialog(self, event = None):
print("Запрос обновления сообщений")
if not self.is_msg_update_blocked:
self.is_msg_update_blocked = True
messages = await self.telegram_client.get_messages(
entity=self.chat_id, limit=self.limit
)
print("Получены сообщения")
limit = len(messages)
self.mount_messages(limit)
for i in range(limit):
chat = self.dialog.query_one(f"#msg-{i + 1}")
chat.message = str(messages[i].message) + \
(not str(messages[i].message)) * " "
chat.is_me = messages[i].from_id == self.me.id
chat._update_styles()
self.is_msg_update_blocked = False
print("Сообщения обновлены")
else:
print("Обновление сообщений невозможно: уже выполняется")
def compose(self): def compose(self):
with Vertical(): with Vertical():
with VerticalScroll(id="dialog"): yield 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"): 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")
def on_button_pressed(self, event): # self добавил async def on_button_pressed(self, event = None):
self.app.notify("Нажато отправить") await self.send_message()
async def on_input_submitted(self, event = None):
await self.send_message()
async def send_message(self):
await self.telegram_client.send_message(
self.chat_id,
str(self.msg_input.value)
)
self.msg_input.value = ""
await self.update_dialog()
class Message(Widget): class Message(Widget):
"""Класс виджета сообщений для окна диалога""" """Класс виджета сообщений для окна диалога"""
message = Reactive("", recompose=True)
is_me = Reactive(False, recompose=True)
def __init__( def __init__(
self, self,
@ -84,18 +151,17 @@ class Message(Widget):
def on_mount(self): def on_mount(self):
container = self.query_one(Container) container = self.query_one(Container)
label = container.query_one(Label) label_border = container.query_one(".border")
if self.is_me: if self.is_me:
self.styles.padding = (0, 0, 0, 15) self.styles.padding = (0, 0, 0, 15)
label.styles.text_align = "right"
container.styles.align_horizontal = "right" container.styles.align_horizontal = "right"
label.styles.border = ("solid", "#4287f5") label_border.styles.border = ("solid", "#4287f5")
else: else:
self.styles.padding = (0, 15, 0, 0) self.styles.padding = (0, 15, 0, 0)
label.styles.text_align = "left"
container.styles.align_horizontal = "left" container.styles.align_horizontal = "left"
label.styles.border = ("solid", "#ffffff") label_border.styles.border = ("solid", "#ffffff")
def compose(self): def compose(self):
with Container(): with Container():
yield Label(str(self.message)) with Static(classes="border"):
yield Static(str(self.message))

View File

@ -1,4 +1,4 @@
"""Получите свои API-ключи на https://my.telegram.org/apps""" """Получите свои API-ключи на https://my.telegram.org/apps"""
api_id = 12345 api_id = 12345
api_hash = "0123456789abcdef" api_hash = "0123456789abcdef"