mirror of
https://github.com/avitoras/telegram-tui.git
synced 2025-07-27 11:20:31 +00:00
Добавлена локализация, теперь приложение также переведено на английский
This commit is contained in:
parent
83f4cac86b
commit
7ecdf5ea39
@ -10,6 +10,6 @@ API_HASH=b18441a1ff607e10a989891a5462e627
|
||||
|
||||
# Конфиг
|
||||
CURRENT_USER = "user"
|
||||
DO_NOTIFY = False
|
||||
DO_NOTIFY = 0 # Linux-only for now
|
||||
UTC_OFFSET = 0
|
||||
LANGUAGE = "en"
|
||||
LANGUAGE = "en" # en, ru
|
||||
|
@ -31,7 +31,7 @@ pip install -r requirements.txt
|
||||
4. Настройте переменные окружения:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Отредактируйте .env файл, добавив свои API ключи
|
||||
# Настройте .env файл и добавьте свои API ключи
|
||||
# Получите ключи на https://my.telegram.org/apps
|
||||
```
|
||||
|
||||
|
42
src/app.py
42
src/app.py
@ -4,30 +4,60 @@ from os import getenv
|
||||
from dotenv import load_dotenv
|
||||
from telethon import TelegramClient
|
||||
from textual.app import App
|
||||
from textual.keys import Keys, _character_to_key
|
||||
from datetime import timezone, timedelta
|
||||
from src.screens import AuthScreen, ChatScreen
|
||||
import src.locales
|
||||
|
||||
load_dotenv()
|
||||
API_ID = getenv("API_ID")
|
||||
API_HASH = getenv("API_HASH")
|
||||
LANGUAGE = getenv("LANGUAGE")
|
||||
UTC_OFFSET = getenv("UTC_OFFSET")
|
||||
|
||||
if not API_ID or not API_HASH:
|
||||
if "" in [API_ID, API_HASH, LANGUAGE, UTC_OFFSET]:
|
||||
raise ValueError(
|
||||
"API_ID и API_HASH не найдены в .env файле. "
|
||||
"Пожалуйста, скопируйте .env.example в .env и заполните свои ключи."
|
||||
"Недостаточно параметров в .env файле."
|
||||
"Скопируйте .env.example в .env и заполните свои API-ключи."
|
||||
"Not enough settings in .env file."
|
||||
"Copy .env.example into .env and fill your API-keys."
|
||||
)
|
||||
|
||||
API_ID = int(API_ID)
|
||||
|
||||
#locale = locales.
|
||||
locale = dict(zip(
|
||||
getattr(src.locales, "codes"), getattr(src.locales, LANGUAGE)
|
||||
))
|
||||
|
||||
class Talc(App):
|
||||
"""Класс приложения"""
|
||||
|
||||
CSS_PATH = "style.tcss"
|
||||
BINDINGS = [
|
||||
(Keys.ControlE, "notify(\"Нажата кнопка профиля\")", locale["you"]),
|
||||
(Keys.ControlF, "notify(\"Нажата кнопка папок\")", locale["folders"]),
|
||||
(Keys.Tab, "notify(\"Нажат таб\")", locale["switch_focus"]),
|
||||
(Keys.Enter, "notify(\"Нажат энтер\")", locale["enter"]),
|
||||
(Keys.Escape, "notify(\"Нажат эскейп\")", locale["back"]),
|
||||
(_character_to_key("/"), "notify(\"Нажат слэш\")", locale["search"])
|
||||
]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
driver_class = None,
|
||||
css_path = None,
|
||||
watch_css = False,
|
||||
ansi_color = False
|
||||
):
|
||||
super().__init__(driver_class, css_path, watch_css, ansi_color)
|
||||
self.locale = locale
|
||||
self.timezone = timezone(timedelta(hours=int(UTC_OFFSET)))
|
||||
|
||||
async def on_mount(self) -> None:
|
||||
self.telegram_client = TelegramClient(getenv("CURRENT_USER"), API_ID, API_HASH)
|
||||
self.telegram_client = TelegramClient(
|
||||
getenv("CURRENT_USER"),
|
||||
API_ID,
|
||||
API_HASH
|
||||
)
|
||||
await self.telegram_client.connect()
|
||||
|
||||
chat_screen = ChatScreen(telegram_client=self.telegram_client)
|
||||
|
@ -1,2 +1,3 @@
|
||||
ru = {"greeting_auth": "Добро пожаловать в Тальк", "phone_number": "Номер телефона", "code": "Код", "password": "Пароль", "you": "Вы", "mention": "Вас упомянули"}
|
||||
en = {"greeting_auth": "Welcome to Talc", "phone_number": "Phone number", "code": "Code", "password": "Password", "you": "You", "mention": "You got mentioned"}
|
||||
codes = ["auth_greeting", "phone_number", "code", "password", "you", "mention", "media", "message", "folders", "switch_focus", "enter", "back", "search", "start_converse"]
|
||||
ru = ["Добро пожаловать в Тальк", "Номер телефона", "Код", "Пароль", "Вы", "Вас упомянули", "Медиа", "Сообщение", "Папки", "Переключение фокуса", "Открыть", "Назад", "Поиск", "Нажмите на чат в панели слева, чтобы начать общаться"]
|
||||
en = ["Welcome to Talc", "Phone number", "Code", "Password", "You", "You got mentioned", "Media", "Message", "Folders", "Switch focus", "Enter", "Back", "Search", "Click on the chat to start conversation"]
|
@ -22,17 +22,18 @@ class AuthScreen(Screen):
|
||||
) -> None:
|
||||
super().__init__(name, id, classes)
|
||||
self.client = telegram_client
|
||||
self.locale = self.app.locale
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.ac = self.query_one("#auth_container")
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Vertical(id="auth_container"):
|
||||
yield Label("Добро пожаловать в Talc")
|
||||
yield Input(placeholder="Номер телефона", id="phone")
|
||||
yield Input(placeholder="Код", id="code", disabled=True)
|
||||
yield Label(self.locale["auth_greeting"])
|
||||
yield Input(placeholder=self.locale["phone_number"], id="phone")
|
||||
yield Input(placeholder=self.locale["code"], id="code", disabled=True)
|
||||
yield Input(
|
||||
placeholder="Пароль",
|
||||
placeholder=self.locale["password"],
|
||||
id="password",
|
||||
password=True,
|
||||
disabled=True
|
||||
@ -75,6 +76,7 @@ class ChatScreen(Screen):
|
||||
super().__init__(name, id, classes)
|
||||
self.telegram_client = telegram_client
|
||||
self.DO_NOTIFY = getenv("DO_NOTIFY")
|
||||
self.locale = self.app.locale
|
||||
|
||||
async def on_mount(self) -> None:
|
||||
self.limit = 100
|
||||
@ -152,6 +154,7 @@ class ChatScreen(Screen):
|
||||
|
||||
chat.peername = str(dialogs[i].name)
|
||||
chat.is_group = dialogs[i].is_group
|
||||
chat.is_channel = dialogs[i].is_channel
|
||||
chat.peer_id = dialogs[i].id
|
||||
|
||||
try:
|
||||
@ -161,7 +164,7 @@ class ChatScreen(Screen):
|
||||
is_my_msg = dialogs[i].id == self.me_id
|
||||
|
||||
if dialogs[i].is_group and is_my_msg:
|
||||
chat.username = "Вы"
|
||||
chat.username = self.locale["you"]
|
||||
chat.msg = str(dialogs[i].message.message)
|
||||
elif dialogs[i].is_group:
|
||||
chat.username = str(
|
||||
@ -169,7 +172,7 @@ class ChatScreen(Screen):
|
||||
)
|
||||
chat.msg = str(dialogs[i].message.message)
|
||||
elif is_my_msg:
|
||||
chat.msg = "Вы: " * is_my_msg + str(
|
||||
chat.msg = f"{self.locale["you"]}: " * is_my_msg + str(
|
||||
dialogs[i].message.message
|
||||
)
|
||||
else:
|
||||
@ -183,8 +186,8 @@ class ChatScreen(Screen):
|
||||
def notify_send(self, event) -> None:
|
||||
if not event:
|
||||
return None
|
||||
if bool(self.DO_NOTIFY) and event.mentioned and not self.app.focused:
|
||||
system(f"notify-send \"Вас упомянули\" Talc")
|
||||
if int(self.DO_NOTIFY) and not self.app.focused and event.mentioned:
|
||||
system(f"notify-send \"{self.locale["mention"]}\" Talc")
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Footer() # Нижняя панель с подсказками
|
||||
@ -196,6 +199,6 @@ class ChatScreen(Screen):
|
||||
with ContentSwitcher(id="dialog_switcher"):
|
||||
# ↑ Внутри него как раз крутятся диалоги
|
||||
yield Label(
|
||||
"Нажмите на чат в панели слева, чтобы начать общаться",
|
||||
id="begin_talk_label"
|
||||
self.locale["start_converse"],
|
||||
id="start_converse_label"
|
||||
) #TODO: не показывается надпись, надо будет исправить
|
||||
|
@ -74,7 +74,7 @@ ContentSwitcher {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.begin_talk_label {
|
||||
.start_converse_label {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
|
@ -6,6 +6,7 @@ from textual.reactive import Reactive
|
||||
from textual.widgets import Input, Button, Label, Static, ContentSwitcher
|
||||
from textual.app import ComposeResult, RenderResult
|
||||
from textual.content import Content
|
||||
from textual.style import Style
|
||||
from telethon import TelegramClient, events, utils, types
|
||||
import datetime
|
||||
from os import getenv
|
||||
@ -17,6 +18,7 @@ class Chat(Widget):
|
||||
peername: Reactive[str] = Reactive(" ", recompose=True)
|
||||
msg: Reactive[str] = Reactive(" ", recompose=True)
|
||||
is_group: Reactive[bool] = Reactive(False, recompose=True)
|
||||
is_channel: Reactive[bool] = Reactive(False, recompose=True)
|
||||
peer_id: Reactive[int] = Reactive(0)
|
||||
|
||||
def __init__(
|
||||
@ -46,7 +48,8 @@ class Chat(Widget):
|
||||
self.switcher.mount(Dialog(
|
||||
telegram_client=self.app.telegram_client,
|
||||
chat_id=self.peer_id,
|
||||
id=dialog_id
|
||||
id=dialog_id,
|
||||
is_channel=self.is_channel and not self.is_group
|
||||
))
|
||||
except:
|
||||
# Диалог уже есть: ничего не делаем
|
||||
@ -59,10 +62,10 @@ class Chat(Widget):
|
||||
with Horizontal():
|
||||
yield Label(f"┌───┐\n│ {self.peername[:1]:1} │\n└───┘")
|
||||
with Vertical():
|
||||
yield Label(self.peername, id="peername")
|
||||
yield Label(self.peername, id="peername", markup=False)
|
||||
if self.is_group:
|
||||
yield Label(self.username, id="name")
|
||||
yield Label(self.msg, id="last_msg")
|
||||
yield Label(self.username, id="name", markup=False)
|
||||
yield Label(self.msg, id="last_msg", markup=False)
|
||||
|
||||
class Dialog(Widget):
|
||||
"""Класс окна диалога"""
|
||||
@ -73,19 +76,20 @@ class Dialog(Widget):
|
||||
classes=None,
|
||||
disabled=None,
|
||||
telegram_client: TelegramClient | None = None,
|
||||
chat_id = None
|
||||
chat_id: int | None = None,
|
||||
is_channel: bool | None = None
|
||||
) -> None:
|
||||
super().__init__(id=id, classes=classes, disabled=disabled)
|
||||
self.telegram_client = telegram_client
|
||||
self.chat_id = chat_id
|
||||
self.is_msg_update_blocked = False
|
||||
self.timezone = datetime.timezone(
|
||||
datetime.timedelta(hours=int(getenv("UTC_OFFSET")))
|
||||
)
|
||||
self.timezone = self.app.timezone
|
||||
self.is_channel = is_channel
|
||||
|
||||
async def on_mount(self) -> None:
|
||||
self.limit = 10
|
||||
|
||||
if not self.is_channel:
|
||||
self.msg_input = self.query_one("#msg_input")
|
||||
self.dialog = self.query_one(Vertical).query_one("#dialog")
|
||||
|
||||
@ -102,7 +106,7 @@ class Dialog(Widget):
|
||||
event(chats=(self.chat_id))
|
||||
)(self.update_dialog)
|
||||
|
||||
self.dialog.scroll_down(animate=False, immediate=True)
|
||||
#self.dialog.scroll_down(animate=False, immediate=True)
|
||||
|
||||
def mount_messages(self, limit: int) -> None:
|
||||
print("Загрузка виджетов сообщений...")
|
||||
@ -135,15 +139,8 @@ class Dialog(Widget):
|
||||
|
||||
for i in range(limit):
|
||||
msg = self.dialog.query_one(f"#msg-{i + 1}")
|
||||
message = Content(str(messages[i].message))
|
||||
if str(messages[i].message):
|
||||
message = Content(
|
||||
"[Медиа] " * \
|
||||
bool(messages[i].media) + \
|
||||
str(messages[i].message)
|
||||
)
|
||||
else:
|
||||
message = Content("[Медиа]" * bool(messages[i].media) + "")
|
||||
|
||||
entities = messages[i].entities
|
||||
if entities:
|
||||
for entity in entities:
|
||||
@ -173,6 +170,13 @@ class Dialog(Widget):
|
||||
entity.offset + entity.length
|
||||
)
|
||||
|
||||
if messages[i].media and str(message):
|
||||
msg.message = Content(f"[{self.app.locale["media"]}] ")\
|
||||
.stylize("link", 0, 6) + message
|
||||
elif messages[i].media:
|
||||
msg.message = Content(f"[{self.app.locale["media"]}]")\
|
||||
.stylize("link", 0, 6)
|
||||
else:
|
||||
msg.message = message
|
||||
|
||||
try:
|
||||
@ -195,8 +199,12 @@ class Dialog(Widget):
|
||||
def compose(self) -> ComposeResult:
|
||||
with Vertical():
|
||||
yield VerticalScroll(id="dialog")
|
||||
if not self.is_channel:
|
||||
with Horizontal(id="input_place"):
|
||||
yield Input(placeholder="Сообщение", id="msg_input")
|
||||
yield Input(
|
||||
placeholder=self.app.locale["message"],
|
||||
id="msg_input"
|
||||
)
|
||||
yield Button(label="➤", id="send", variant="primary")
|
||||
|
||||
async def on_button_pressed(self, event = None) -> None:
|
||||
@ -212,7 +220,7 @@ class Dialog(Widget):
|
||||
str(self.msg_input.value)
|
||||
)
|
||||
except ValueError:
|
||||
self.app.notify("Ошибка отправки")
|
||||
print("Ошибка отправки")
|
||||
self.msg_input.value = ""
|
||||
await self.update_dialog()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user