From 051add865a31bd635bea09dadc57fea1d085ee69 Mon Sep 17 00:00:00 2001 From: kirill Date: Sun, 26 Jan 2025 13:42:14 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BD,=20=D0=B0=20=D1=82?= =?UTF-8?q?=D0=B0=D0=BA=D0=B6=D0=B5=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C?= =?UTF-8?q?=20=D0=B2=D1=81=D0=B5=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B2=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0=D1=85=20=D0=BD=D0=B5=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B2=D1=8B=D1=88=D0=B0=D1=8E=D1=82=20=D0=BB?= =?UTF-8?q?=D0=B8=D0=BC=D0=B8=D1=82=20=D0=B2=2079=20=D1=81=D0=B8=D0=BC?= =?UTF-8?q?=D0=B2=D0=BE=D0=BB=D0=BE=D0=B2=20(PEP=208)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/app.py | 102 +++++------------------------------------ screens/auth_screen.py | 56 ++++++++++++++++++++++ screens/chat_screen.py | 64 ++++++++++++++++++++++++++ telegram/client.py | 5 ++ tokens.py | 6 ++- widgets/chat.py | 16 ++++++- widgets/dialog.py | 10 +++- widgets/message.py | 10 +++- 8 files changed, 171 insertions(+), 98 deletions(-) create mode 100644 screens/auth_screen.py create mode 100644 screens/chat_screen.py diff --git a/app/app.py b/app/app.py index ad2477f..3ddbf85 100644 --- a/app/app.py +++ b/app/app.py @@ -1,91 +1,15 @@ from telethon import TelegramClient, events +from telethon.errors import SessionPasswordNeededError from textual.app import App, ComposeResult from textual.containers import Horizontal, VerticalScroll, Vertical from textual.widgets import Static, Footer, Label, Input, Button +from textual.screen import Screen +from textual.events import Event from widgets.chat import Chat from widgets.dialog import Dialog -from textual.screen import Screen -#from telegram.client import TelegramClientWrapper from tokens import api_id, api_hash - -class ChatScreen(Screen): - """Класс экрана чатов, он же основной экран приложения""" - - def __init__(self, name = None, id = None, classes = None, telegram_client = None): - super().__init__(name, id, classes) - self.telegram_client = telegram_client - self.telegram_client.on(events.NewMessage())(self.update_chat_list) - - def on_mount(self): - self.chat_container = self.query_one("#main_container").query_one("#chats").query_one("#chat_container") - - self.limit = 100 - for i in range(self.limit): - chat = Chat(id=f"chat-{i + 1}", notify_func=self.notify) - self.chat_container.mount(chat) - #self.mount_chats(limit=25) - - def mount_chats(self, limit: int): - self.limit = limit - 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 + 1 + (limit - chats_amount)}") - self.chat_container.mount(chat) - elif not (limit == chats_amount): - for i in range(chats_amount - limit): - self.chat_container.query(Chat).last().remove() - - async def update_chat_list(self): - dialogs = await self.telegram_client.get_dialogs(limit=self.limit) - - for i in range(len(dialogs)): - 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("Новое сообщение") #колхоз дебаг - - def compose(self) -> ComposeResult: - yield Footer() - with Horizontal(id="main_container"): - with Horizontal(id="chats"): - yield VerticalScroll(Static(id="chat_container")) - #TODO: сделать кнопку чтобы прогрузить больше чатов, это оптимизация - - yield Dialog() - -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 - 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_submit(self, event: Input.Submitted) -> None: - if event.button.id == "phone": - await self.client.send_code_request(event.value) - for i in ("#phone", "#password"): - self.ac.query_one(i).disabled = True - self.ac.query_one("#code").disabled = False - elif event.button.id == "code": - await self.client.sign_in(event.value) - for i in ("#phone", "#code"): - self.ac.query_one(i).disabled = True - self.ac.query_one("#password").disabled = False - elif event.button.id == "password": - await self.client.sign_in(event.value) - for i in ("#phone", "#password"): - self.ac.query_one(i).disabled = True - self.ac.query_one("#code").disabled = True +from screens.auth_screen import AuthScreen +from screens.chat_screen import ChatScreen class TelegramTUI(App): """Класс приложения""" @@ -93,23 +17,19 @@ class TelegramTUI(App): CSS_PATH = "../tcss/style.tcss" #SCREENS = {"chats": ChatScreen} - def __init__(self): - super().__init__() - async def on_mount(self) -> None: self.telegram_client = TelegramClient("user", api_id, api_hash) - await self.telegram_client.start() + 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") - - """chat_screen = ChatScreen(telegram_client=self.telegram_client) - self.install_screen(chat_screen, name="chats") - self.push_screen("chats") - await self.telegram_client.start() - await chat_screen.update_chat_list()""" + else: + self.push_screen("chats") async def on_exit_app(self): await self.telegram_client.disconnect() diff --git a/screens/auth_screen.py b/screens/auth_screen.py new file mode 100644 index 0000000..4406c4c --- /dev/null +++ b/screens/auth_screen.py @@ -0,0 +1,56 @@ +from textual.screen import Screen +from textual.widgets import Label, Input +from textual.containers import Vertical +from telethon import TelegramClient +from telethon.errors import SessionPasswordNeededError + +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") diff --git a/screens/chat_screen.py b/screens/chat_screen.py new file mode 100644 index 0000000..6e58992 --- /dev/null +++ b/screens/chat_screen.py @@ -0,0 +1,64 @@ +from textual.screen import Screen +from textual.widgets import Footer, Static +from textual.containers import Horizontal, VerticalScroll +from telethon import TelegramClient, events +from widgets.dialog import Dialog +from widgets.chat import Chat + +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 + self.telegram_client.on(events.NewMessage())(self.update_chat_list) + + async def on_mount(self): + self.chat_container = self\ + .query_one("#main_container")\ + .query_one("#chats")\ + .query_one("#chat_container") + + self.limit = 100 + for i in range(self.limit): + chat = Chat(id=f"chat-{i + 1}", notify_func=self.notify) + self.chat_container.mount(chat) + #self.mount_chats(limit=25) + + await self.update_chat_list() + + def mount_chats(self, limit: int): + self.limit = limit + 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 + 1 + (limit - chats_amount)}") + self.chat_container.mount(chat) + elif not (limit == chats_amount): + for i in range(chats_amount - limit): + self.chat_container.query(Chat).last().remove() + + async def update_chat_list(self): + dialogs = await self.telegram_client.get_dialogs(limit=self.limit) + + for i in range(len(dialogs)): + 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("Новое сообщение") #колхоз дебаг + + def compose(self): + yield Footer() + with Horizontal(id="main_container"): + with Horizontal(id="chats"): + yield VerticalScroll(Static(id="chat_container")) + #TODO: сделать кнопку чтобы прогрузить больше чатов + + yield Dialog() diff --git a/telegram/client.py b/telegram/client.py index 59ff039..1010434 100644 --- a/telegram/client.py +++ b/telegram/client.py @@ -1,3 +1,8 @@ +""" +ЭТОТ ФАЙЛ БОЛЬШЕ НЕ ИСПОЛЬЗУЕТСЯ +СКОРО УДАЛИМ +""" + from telethon import TelegramClient, events, utils class TelegramClientWrapper: diff --git a/tokens.py b/tokens.py index 1c6c644..2d34b43 100644 --- a/tokens.py +++ b/tokens.py @@ -1,2 +1,4 @@ -api_hash = 111 -api_id = 11 +"""Получите свои API-ключи на https://my.telegram.org/apps""" + +api_id = 12345 +api_hash = "0123456789abcdef" \ No newline at end of file diff --git a/widgets/chat.py b/widgets/chat.py index 09dfdc0..13b9a7c 100644 --- a/widgets/chat.py +++ b/widgets/chat.py @@ -10,8 +10,20 @@ class Chat(Widget): 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) + 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): diff --git a/widgets/dialog.py b/widgets/dialog.py index e8ed1b6..05a9fe0 100644 --- a/widgets/dialog.py +++ b/widgets/dialog.py @@ -15,9 +15,15 @@ class Dialog(Widget): 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") + # должно быть примерно + # is_me = message.from_id == client.get_peer_id("me") + # но я могу ошибаться, я это фиш если что - #TODO: сделать кнопку чтобы прогрузить больше сообщений, но при этом чтобы при перезаходе в чат оставались прогруженными только 10 сообщений, а остальные декомпоузились + + #TODO: сделать кнопку чтобы прогрузить больше сообщений, + #но при этом чтобы при перезаходе в чат оставались + #прогруженными только 10 сообщений, + #а остальные декомпоузились with Horizontal(id="input_place"): yield Input(placeholder="Сообщение", id="msg_input") diff --git a/widgets/message.py b/widgets/message.py index edda533..88468e0 100644 --- a/widgets/message.py +++ b/widgets/message.py @@ -5,7 +5,15 @@ from textual.widget import Widget class Message(Widget): """Класс виджета сообщений для окна диалога""" - def __init__(self, name=None, message=None, is_me=None, id=None, classes=None, disabled=False): + 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