mirror of
https://github.com/avitoras/telegram-tui.git
synced 2025-07-27 19:26:10 +00:00
Compare commits
No commits in common. "2602fd2680f8c87edef36123e788c5504e9511d3" and "bcd1ec6fbee1e33ba2a44c9113639866bcde3c24" have entirely different histories.
2602fd2680
...
bcd1ec6fbe
2
main.py
2
main.py
@ -1,5 +1,3 @@
|
|||||||
"""Файл инициализации приложения"""
|
|
||||||
|
|
||||||
from src.app import TelegramTUI
|
from src.app import TelegramTUI
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
"""Главный файл приложения"""
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
"""Файл с кастомными экранами приложения"""
|
|
||||||
|
|
||||||
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
|
from telethon import TelegramClient, events, utils
|
||||||
from src.widgets import Dialog, Chat
|
from src.widgets import Dialog, Chat
|
||||||
|
|
||||||
class AuthScreen(Screen):
|
class AuthScreen(Screen):
|
||||||
"""Класс экрана логина в аккаунт"""
|
"""Класс логина в аккаунт"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -16,7 +14,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
|
||||||
|
|
||||||
@ -57,6 +55,7 @@ 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):
|
||||||
"""Класс экрана чатов, он же основной экран приложения"""
|
"""Класс экрана чатов, он же основной экран приложения"""
|
||||||
@ -95,11 +94,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("Загрузка виджетов чатов...")
|
||||||
@ -118,16 +117,14 @@ 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):
|
||||||
@ -135,6 +132,7 @@ 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("Чаты обновлены")
|
||||||
@ -145,6 +143,7 @@ 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(id="chat_container")
|
yield VerticalScroll(Static(id="chat_container"))
|
||||||
#TODO: сделать кнопку чтобы прогрузить больше чатов
|
#TODO: сделать кнопку чтобы прогрузить больше чатов
|
||||||
yield Dialog(telegram_client=self.telegram_client)
|
|
||||||
|
yield Dialog()
|
||||||
|
@ -28,11 +28,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%;
|
||||||
@ -43,6 +38,10 @@ Message Static {
|
|||||||
width: 65%;
|
width: 65%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#send {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#auth_container{
|
#auth_container{
|
||||||
align: center middle;
|
align: center middle;
|
||||||
}
|
}
|
||||||
|
124
src/widgets.py
124
src/widgets.py
@ -1,10 +1,7 @@
|
|||||||
"""Файл с кастомными виджетами приложения"""
|
|
||||||
|
|
||||||
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
|
||||||
from telethon import TelegramClient, events
|
|
||||||
|
|
||||||
class Chat(Widget):
|
class Chat(Widget):
|
||||||
"""Класс виджета чата для панели чатов"""
|
"""Класс виджета чата для панели чатов"""
|
||||||
@ -16,20 +13,22 @@ 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.app.notify("нажат чат")
|
self.notify("нажат чат")
|
||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
with Horizontal():
|
with Horizontal():
|
||||||
@ -41,100 +40,34 @@ class Chat(Widget):
|
|||||||
class Dialog(Widget):
|
class Dialog(Widget):
|
||||||
"""Класс окна диалога"""
|
"""Класс окна диалога"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, id=None, classes=None, disabled=False):
|
||||||
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():
|
||||||
yield VerticalScroll(id="dialog")
|
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"):
|
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):
|
def on_button_pressed(self, event): # self добавил
|
||||||
await self.send_message()
|
self.app.notify("Нажато отправить")
|
||||||
|
|
||||||
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,
|
||||||
@ -151,17 +84,18 @@ class Message(Widget):
|
|||||||
|
|
||||||
def on_mount(self):
|
def on_mount(self):
|
||||||
container = self.query_one(Container)
|
container = self.query_one(Container)
|
||||||
label_border = container.query_one(".border")
|
label = container.query_one(Label)
|
||||||
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_border.styles.border = ("solid", "#4287f5")
|
label.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_border.styles.border = ("solid", "#ffffff")
|
label.styles.border = ("solid", "#ffffff")
|
||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
with Container():
|
with Container():
|
||||||
with Static(classes="border"):
|
yield Label(str(self.message))
|
||||||
yield Static(str(self.message))
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user