From 051c4a0265fe21935b4eacf87ee0fd6dd8475765 Mon Sep 17 00:00:00 2001 From: Mikhail Kilin Date: Wed, 28 Jan 2026 01:29:03 +0300 Subject: [PATCH] fixes --- .editorconfig | 37 +++ .github/ISSUE_TEMPLATE/bug_report.md | 40 +++ .github/ISSUE_TEMPLATE/feature_request.md | 34 +++ .github/pull_request_template.md | 51 ++++ .github/workflows/ci.yml | 50 +++ .gitignore | 4 + CHANGELOG.md | 66 ++++ CONTEXT.md | 67 +++- CONTRIBUTING.md | 125 ++++++++ Cargo.lock | 191 +++++++++++- Cargo.toml | 8 + FAQ.md | 227 ++++++++++++++ HOTKEYS.md | 144 +++++++++ INSTALL.md | 122 ++++++++ LICENSE | 21 ++ PROJECT_STRUCTURE.md | 355 ++++++++++++++++++++++ README.md | 164 +++++++++- REQUIREMENTS.md | 54 +++- ROADMAP.md | 4 +- SECURITY.md | 64 ++++ config.toml.example | 31 ++ credentials.example | 10 + rustfmt.toml | 21 ++ src/app/mod.rs | 4 +- src/config.rs | 265 ++++++++++++++++ src/main.rs | 6 +- src/tdlib/client.rs | 15 +- src/ui/messages.rs | 18 +- src/utils.rs | 36 ++- 29 files changed, 2189 insertions(+), 45 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/ci.yml create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 FAQ.md create mode 100644 HOTKEYS.md create mode 100644 INSTALL.md create mode 100644 LICENSE create mode 100644 PROJECT_STRUCTURE.md create mode 100644 SECURITY.md create mode 100644 config.toml.example create mode 100644 credentials.example create mode 100644 rustfmt.toml create mode 100644 src/config.rs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0a5a199 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,37 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true + +# Rust files +[*.rs] +indent_style = space +indent_size = 4 + +# TOML files +[*.toml] +indent_style = space +indent_size = 2 + +# Markdown files +[*.md] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false + +# YAML files +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +# JSON files +[*.json] +indent_style = space +indent_size = 2 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..e4acc8b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug Report +about: Сообщить о проблеме или баге +title: '[BUG] ' +labels: bug +assignees: '' +--- + +## Описание бага +Четкое и краткое описание проблемы. + +## Шаги для воспроизведения +1. Запустить '...' +2. Нажать на '...' +3. Прокрутить вниз до '...' +4. Увидеть ошибку + +## Ожидаемое поведение +Что должно было произойти. + +## Фактическое поведение +Что произошло на самом деле. + +## Скриншоты +Если применимо, добавьте скриншоты для демонстрации проблемы. + +## Окружение +- **ОС**: [например, macOS 14.0, Ubuntu 22.04, Windows 11] +- **Rust версия**: [вывод `rustc --version`] +- **tele-tui версия**: [вывод `cargo pkgid`] +- **Размер терминала**: [например, 100x30] + +## Логи +Если есть логи или сообщения об ошибках, вставьте их сюда: +``` +вставьте логи здесь +``` + +## Дополнительный контекст +Любая другая информация, которая может помочь в решении проблемы. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..c5702cd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,34 @@ +--- +name: Feature Request +about: Предложить новую функцию или улучшение +title: '[FEATURE] ' +labels: enhancement +assignees: '' +--- + +## Связано с проблемой? +Есть ли проблема, которую это решит? Например: "Меня расстраивает, что [...]" + +## Описание решения +Четкое и краткое описание того, что вы хотите. + +## Альтернативы +Какие альтернативные решения или функции вы рассматривали? + +## Примеры использования +Как эта функция будет использоваться? Приведите примеры: + +1. Пользователь делает X +2. Система делает Y +3. Результат: Z + +## Приоритет +- [ ] Критичная функция — без неё приложение малополезно +- [ ] Важная функция — значительно улучшит UX +- [ ] Nice to have — было бы удобно + +## Проверка roadmap +- [ ] Я проверил [ROADMAP.md](../ROADMAP.md) и этой функции там нет + +## Дополнительный контекст +Скриншоты, ссылки на похожие реализации в других приложениях, и т.д. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..caaf515 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,51 @@ +## Описание + +Краткое описание изменений в этом PR. + +## Тип изменений + +- [ ] Bug fix (исправление бага) +- [ ] New feature (новая функция) +- [ ] Breaking change (изменение, ломающее обратную совместимость) +- [ ] Refactoring (рефакторинг без изменения функциональности) +- [ ] Documentation (изменения в документации) +- [ ] Performance improvement (улучшение производительности) + +## Связанные Issue + +Fixes #(номер issue) + +## Как протестировано? + +Опишите тесты, которые вы провели: + +- [ ] Тест A +- [ ] Тест B +- [ ] Тест C + +## Сценарии тестирования + +Подробные шаги для проверки изменений: + +1. Запустить `cargo run` +2. Сделать X +3. Убедиться, что Y + +## Чеклист + +- [ ] Мой код следует стилю проекта +- [ ] Я запустил `cargo fmt` +- [ ] Я запустил `cargo clippy` и исправил warnings +- [ ] Код компилируется без ошибок (`cargo build`) +- [ ] Я протестировал изменения вручную +- [ ] Я обновил документацию (если необходимо) +- [ ] Я добавил тесты (если применимо) +- [ ] Все существующие тесты проходят + +## Скриншоты (если применимо) + +Добавьте скриншоты для демонстрации UI изменений. + +## Дополнительные заметки + +Любая дополнительная информация для ревьюверов. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f3369cd --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: CI + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +env: + CARGO_TERM_COLOR: always + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo check --all-features + + fmt: + name: Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt --all -- --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - run: cargo clippy --all-features -- -D warnings + + build: + name: Build + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo build --release --all-features diff --git a/.gitignore b/.gitignore index bb9e215..fd2f35f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ # Environment variables (contains API keys) .env .DS_Store + +# Local config files (if created in project root) +config.toml +credentials diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..712a3c5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,66 @@ +# Changelog + +Все значительные изменения в этом проекте будут документированы в этом файле. + +Формат основан на [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +и этот проект придерживается [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.1.0] - 2024-12-XX + +### Добавлено + +#### Базовая функциональность +- TDLib интеграция с авторизацией (телефон + код + 2FA) +- Отображение списка чатов с поддержкой папок +- Загрузка и отображение истории сообщений +- Отправка текстовых сообщений +- Vim-style навигация (hjkl) с поддержкой русской раскладки (ролд) +- Поиск по чатам (Ctrl+S) +- Поиск внутри чата (Ctrl+F) + +#### Сообщения +- Группировка по дате и отправителю +- Markdown форматирование (жирный, курсив, подчёркивание, зачёркивание, код, спойлеры) +- Редактирование сообщений +- Удаление сообщений с подтверждением +- Reply на сообщения +- Forward сообщений +- Копирование в системный буфер обмена +- Реакции на сообщения с emoji picker + +#### UI/UX +- Индикаторы: онлайн-статус (●), прочитанность (✓/✓✓), редактирование (✎) +- Иконки: 📌 закреплённые чаты, 🔇 замьюченные, @ упоминания +- Typing indicator ("печатает...") +- Закреплённые сообщения +- Профиль пользователя/чата +- Черновики с автосохранением +- Динамический инпут (расширение до 10 строк) +- Блочный курсор с навигацией +- Состояние сети в футере + +#### Конфигурация +- TOML конфигурация (~/.config/tele-tui/config.toml) +- Настройка часового пояса +- Настройка цветовой схемы +- Приоритетная загрузка credentials из XDG config dir + +#### Оптимизации +- 60 FPS рендеринг +- LRU кеширование пользователей (лимит 500) +- Lazy loading имён пользователей +- Лимиты памяти (500 сообщений на чат, 200 чатов) +- Graceful shutdown + +### Изменено +- Время отображается с учётом настроенного timezone + +### Исправлено +- Корректная обработка TDLib updates в отдельном потоке +- Правильное выравнивание для длинных сообщений +- Приоритет обработки input для модалок + +[Unreleased]: https://github.com/your-username/tele-tui/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/your-username/tele-tui/releases/tag/v0.1.0 diff --git a/CONTEXT.md b/CONTEXT.md index 5deb9ca..54c0ceb 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -1,6 +1,6 @@ # Текущий контекст проекта -## Статус: Фаза 9 — Расширенные возможности +## Статус: Фаза 9 — ЗАВЕРШЕНО ### Что сделано @@ -51,6 +51,12 @@ - Emoji picker с сеткой доступных реакций (8 в ряду) - Добавление/удаление реакций (toggle) - Обновление реакций в реальном времени через Update::MessageInteractionInfo +- **Конфигурационный файл** (`~/.config/tele-tui/config.toml`): + - Автоматическое создание дефолтного конфига при первом запуске + - **Настройка timezone**: формат "+03:00" или "-05:00" + - **Настройка цветов**: incoming_message, outgoing_message, selected_message, reaction_chosen, reaction_other + - **Credentials файл** (`~/.config/tele-tui/credentials`): API_ID и API_HASH + - Приоритет загрузки: ~/.config/tele-tui/credentials → .env → сообщение об ошибке с инструкциями - **Кеширование имён пользователей**: имена загружаются асинхронно и обновляются в UI - **Папки Telegram**: загрузка и переключение между папками (1-9) - **Медиа-заглушки**: [Фото], [Видео], [Голосовое], [Стикер], [GIF] и др. @@ -121,6 +127,7 @@ ``` src/ ├── main.rs # Точка входа, event loop, TDLib инициализация, graceful shutdown +├── config.rs # Конфигурация (TOML), загрузка credentials ├── app/ │ ├── mod.rs # App структура и состояние (needs_redraw флаг) │ └── state.rs # AppScreen enum @@ -136,10 +143,10 @@ src/ │ ├── mod.rs # Роутинг ввода │ ├── auth.rs # Обработка ввода на экране авторизации │ └── main_input.rs # Обработка ввода на главном экране -├── utils.rs # Утилиты (disable_tdlib_logs, format_timestamp, format_date, get_day) +├── utils.rs # Утилиты (disable_tdlib_logs, format_timestamp_with_tz, format_date, get_day) └── tdlib/ ├── mod.rs # Модуль экспорта (TdClient, UserOnlineStatus, NetworkState) - └── client.rs # TdClient: авторизация, чаты, сообщения, кеш, NetworkState + └── client.rs # TdClient: авторизация, чаты, сообщения, кеш, NetworkState, ReactionInfo ``` ### Ключевые решения @@ -162,6 +169,10 @@ src/ 9. **Перенос текста**: Длинные сообщения автоматически разбиваются на строки с учётом ширины терминала. Для исходящих — time_mark на последней строке, для входящих — время на первой строке с отступом для остальных. +10. **Конфигурационный файл**: TOML конфиг создаётся автоматически при первом запуске в `~/.config/tele-tui/config.toml`. Поддерживает настройку timezone (применяется к отображению времени через `format_timestamp_with_tz`) и цветовой схемы (парсится в `ratatui::style::Color`). Credentials загружаются с приоритетом: XDG config dir → .env → ошибка с инструкциями. + +11. **Реакции**: Хранятся в `Vec` для каждого сообщения. Обновляются в реальном времени через `Update::MessageInteractionInfo`. Emoji picker использует сетку 8x6 с навигацией стрелками. Приоритет обработки ввода: reaction picker → delete confirmation → остальные модалки (важно для корректной работы Enter/Esc). + ### Зависимости (Cargo.toml) ```toml @@ -173,20 +184,62 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" dotenvy = "0.15" chrono = "0.4" +clipboard = "0.5" +toml = "0.8" +dirs = "5.0" ``` -### Переменные окружения (.env) +### API Credentials +Приоритет загрузки (от высшего к низшему): + +1. **Файл credentials** (`~/.config/tele-tui/credentials`): ``` API_ID=your_api_id API_HASH=your_api_hash ``` -## Что НЕ сделано / TODO (Фаза 9) +2. **Переменные окружения** (`.env` файл в текущей директории): +``` +API_ID=your_api_id +API_HASH=your_api_hash +``` -- [ ] Конфигурационный файл (~/.config/tele-tui/config.toml) +3. Если ничего не найдено — показывается сообщение об ошибке с инструкциями. + +### Конфигурационный файл + +Создаётся автоматически при первом запуске в `~/.config/tele-tui/config.toml`: + +```toml +[general] +# Часовой пояс в формате "+03:00" или "-05:00" +# Применяется к отображению времени сообщений +timezone = "+03:00" + +[colors] +# Цветовая схема (поддерживаемые цвета: black, red, green, yellow, blue, magenta, cyan, gray, white, darkgray, lightred, lightgreen, lightyellow, lightblue, lightmagenta, lightcyan) + +# Цвет входящих сообщений +incoming_message = "white" + +# Цвет исходящих сообщений +outgoing_message = "green" + +# Цвет выбранного сообщения +selected_message = "yellow" + +# Цвет своих реакций (в рамках [👍]) +reaction_chosen = "yellow" + +# Цвет чужих реакций +reaction_other = "gray" +``` + +## Что НЕ сделано / TODO + +Все пункты Фазы 9 завершены! Можно переходить к следующей фазе разработки. ## Известные проблемы 1. При первом запуске нужно пройти авторизацию -2. Время отображается с фиксированным смещением +3 (MSK) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b452d5c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,125 @@ +# Contributing to tele-tui + +Спасибо за интерес к проекту! Мы рады любому вкладу. + +## Как помочь проекту + +### Сообщить о баге + +1. Проверьте, нет ли уже такого issue в [Issues](https://github.com/your-username/tele-tui/issues) +2. Создайте новый issue с описанием: + - Шаги для воспроизведения + - Ожидаемое поведение + - Фактическое поведение + - Версия ОС и Rust + - Логи (если есть) + +### Предложить новую фичу + +1. Проверьте [ROADMAP.md](ROADMAP.md) — возможно, эта фича уже запланирована +2. Создайте issue с меткой `enhancement` +3. Опишите: + - Зачем нужна эта фича + - Как она должна работать + - Примеры использования + +### Внести код + +1. **Fork** репозитория +2. Создайте **feature branch**: `git checkout -b feature/amazing-feature` +3. Прочитайте [DEVELOPMENT.md](DEVELOPMENT.md) для понимания процесса разработки +4. Внесите изменения +5. Протестируйте локально +6. Commit: `git commit -m 'Add amazing feature'` +7. Push: `git push origin feature/amazing-feature` +8. Создайте **Pull Request** + +## Правила кода + +### Стиль кода + +- Используйте `cargo fmt` перед коммитом +- Следуйте [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/) +- Добавляйте комментарии для сложной логики + +### Структура коммитов + +``` +: <краткое описание> + +<подробное описание (опционально)> +``` + +Типы: +- `feat`: новая фича +- `fix`: исправление бага +- `refactor`: рефакторинг без изменения функциональности +- `docs`: изменения в документации +- `style`: форматирование, отступы +- `test`: добавление тестов +- `chore`: обновление зависимостей, конфигурации + +Примеры: +``` +feat: add emoji reactions to messages + +fix: correct timezone offset calculation + +docs: update installation instructions +``` + +### Тестирование + +- Протестируйте вручную все изменения +- Опишите сценарии тестирования в PR +- Убедитесь, что `cargo build` проходит без ошибок +- Убедитесь, что `cargo fmt` и `cargo clippy` не дают предупреждений + +## Процесс Review + +1. Maintainer проверит ваш PR +2. Возможны комментарии и запросы на изменения +3. После одобрения PR будет смержен +4. Ваш вклад появится в следующем релизе + +## Архитектура проекта + +Перед началом работы рекомендуем ознакомиться: + +- [REQUIREMENTS.md](REQUIREMENTS.md) — функциональные требования +- [CONTEXT.md](CONTEXT.md) — текущий статус и архитектурные решения +- [ROADMAP.md](ROADMAP.md) — план развития + +### Структура кода + +``` +src/ +├── main.rs # Event loop, инициализация +├── config.rs # Конфигурация +├── app/ # Состояние приложения +├── ui/ # Отрисовка UI +├── input/ # Обработка ввода +├── utils.rs # Утилиты +└── tdlib/ # TDLib интеграция +``` + +### Ключевые принципы + +1. **Неблокирующий UI**: TDLib updates в отдельном потоке +2. **Оптимизация памяти**: LRU кеши, лимиты на коллекции +3. **Vim-style навигация**: консистентные хоткеи +4. **Graceful degradation**: fallback для отсутствующих данных + +## Code of Conduct + +- Будьте вежливы и уважительны +- Конструктивная критика приветствуется +- Фокус на технических аспектах + +## Вопросы? + +Создайте issue с меткой `question` или свяжитесь с maintainers. + +## Лицензия + +Внося код в этот проект, вы соглашаетесь с лицензией [MIT](LICENSE). diff --git a/Cargo.lock b/Cargo.lock index 3c23f09..f420f16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -434,13 +434,34 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] + [[package]] name = "dirs" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys", + "dirs-sys 0.5.0", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.4.6", + "windows-sys 0.48.0", ] [[package]] @@ -451,7 +472,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.5.2", "windows-sys 0.61.2", ] @@ -1631,6 +1652,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -1639,7 +1671,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -1894,6 +1926,15 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2117,7 +2158,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98c960258301bee0758a669fbe12ad8a97c6e764d2f30c5426eea008eebf2d2" dependencies = [ - "dirs", + "dirs 6.0.0", "futures-channel", "log", "once_cell", @@ -2152,6 +2193,7 @@ dependencies = [ "arboard", "chrono", "crossterm", + "dirs 5.0.1", "dotenvy", "open", "ratatui", @@ -2159,6 +2201,7 @@ dependencies = [ "serde_json", "tdlib-rs", "tokio", + "toml", ] [[package]] @@ -2174,13 +2217,33 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2310,6 +2373,47 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap 2.13.0", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tower" version = "0.5.3" @@ -2648,6 +2752,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -2684,6 +2797,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -2717,6 +2845,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -2729,6 +2863,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -2741,6 +2881,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2765,6 +2911,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -2777,6 +2929,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -2789,6 +2947,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -2801,6 +2965,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -2813,6 +2983,15 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -2990,7 +3169,7 @@ dependencies = [ "memchr", "pbkdf2", "sha1", - "thiserror", + "thiserror 2.0.18", "time", "xz2", "zeroize", diff --git a/Cargo.toml b/Cargo.toml index 6794238..809d884 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,12 @@ name = "tele-tui" version = "0.1.0" edition = "2021" +authors = ["Your Name "] +description = "Terminal UI for Telegram with Vim-style navigation" +license = "MIT" +repository = "https://github.com/your-username/tele-tui" +keywords = ["telegram", "tui", "terminal", "cli"] +categories = ["command-line-utilities"] [dependencies] ratatui = "0.29" @@ -14,6 +20,8 @@ dotenvy = "0.15" chrono = "0.4" open = "5.0" arboard = "3.4" +toml = "0.8" +dirs = "5.0" [build-dependencies] tdlib-rs = { version = "1.1", features = ["download-tdlib"] } diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 0000000..1bbfac1 --- /dev/null +++ b/FAQ.md @@ -0,0 +1,227 @@ +# FAQ — Часто задаваемые вопросы + +## Установка и запуск + +### Где получить API credentials? + +1. Перейдите на https://my.telegram.org/apps +2. Войдите с вашим номером телефона +3. Создайте новое приложение +4. Скопируйте `api_id` и `api_hash` + +### Где хранить credentials? + +**Рекомендуется** (приоритет 1): +```bash +~/.config/tele-tui/credentials +``` + +**Альтернатива** (приоритет 2): +```bash +.env # в корне проекта +``` + +### Ошибка "Telegram API credentials not found!" + +Убедитесь, что вы создали файл credentials (см. выше) с правильным форматом: +``` +API_ID=12345678 +API_HASH=abcdef1234567890abcdef1234567890 +``` + +### Где хранится сессия Telegram? + +В папке `./tdlib_data/` в директории запуска приложения. Эта папка содержит: +- Токены авторизации +- Кеш сообщений +- Другие данные TDLib + +**Важно**: Не удаляйте эту папку, иначе придётся заново авторизоваться. + +## Использование + +### Как переключаться между папками? + +Нажмите клавиши `1-9` для переключения между первыми 9 папками Telegram. + +### Как искать сообщения в чате? + +1. Откройте чат +2. Нажмите `Ctrl+F` +3. Введите поисковый запрос +4. Используйте `n` / `N` для навигации по результатам + +### Как скопировать текст сообщения? + +1. При пустом поле ввода нажмите `↑` для выбора сообщения +2. Нажмите `y` (или `н` на русской раскладке) +3. Текст скопирован в системный буфер обмена + +### Как ответить на сообщение? + +1. Выберите сообщение (`↑` при пустом инпуте) +2. Нажмите `r` (или `к` на русской раскладке) +3. Введите ответ +4. Нажмите `Enter` + +### Как удалить сообщение? + +1. Выберите сообщение +2. Нажмите `d` / `в` / `Delete` +3. Подтвердите удаление: `y` / `Enter` + +### Как добавить реакцию? + +1. Выберите сообщение +2. Нажмите `e` (или `у` на русской раскладке) +3. Выберите emoji стрелками +4. Нажмите `Enter` + +### Почему не работают хоткеи на русской раскладке? + +Убедитесь, что вы используете **русскую раскладку**, а не транслит. Поддерживаемые комбинации: +- `р о л д` → `h j k l` (навигация) +- `к` → `r` (reply) +- `а` → `f` (forward) +- `в` → `d` (delete) +- `н` → `y` (copy) +- `у` → `e` (react) + +## Конфигурация + +### Где находится конфигурационный файл? + +```bash +~/.config/tele-tui/config.toml +``` + +Создаётся автоматически при первом запуске. + +### Как изменить часовой пояс? + +Отредактируйте `~/.config/tele-tui/config.toml`: + +```toml +[general] +timezone = "+05:00" # Ваш часовой пояс +``` + +### Как изменить цветовую схему? + +Отредактируйте секцию `[colors]` в конфиге: + +```toml +[colors] +incoming_message = "cyan" +outgoing_message = "lightgreen" +selected_message = "lightyellow" +``` + +Поддерживаемые цвета: black, red, green, yellow, blue, magenta, cyan, gray, white, darkgray, lightred, lightgreen, lightyellow, lightblue, lightmagenta, lightcyan. + +### Нужно ли перезапускать приложение после изменения конфига? + +Да, изменения в `config.toml` применяются только при запуске приложения. + +## Проблемы + +### Приложение зависает при запуске + +Возможные причины: +1. **Нет интернета**: проверьте подключение +2. **TDLib не может подключиться**: проверьте firewall/прокси +3. **Неверные credentials**: проверьте API_ID и API_HASH + +### Сообщения не загружаются + +1. Проверьте статус сети в футере (внизу экрана) +2. Попробуйте обновить: `Ctrl+R` +3. Перезапустите приложение + +### "Deleted Account" в списке чатов + +Это пользователи, которые удалили свой аккаунт Telegram. Они автоматически фильтруются и не отображаются в списке. + +### Не отображаются медиафайлы + +Медиафайлы (фото, видео, голосовые, стикеры) отображаются как заглушки: [Фото], [Видео], [Голосовое], [Стикер]. Полная поддержка медиа может быть добавлена в будущем. + +### Ошибка компиляции при сборке + +**TDLib download failed**: +- Проверьте интернет-соединение +- Убедитесь, что у вас достаточно места на диске + +**Linking with cc failed**: +- macOS: `xcode-select --install` +- Linux: `sudo apt-get install build-essential` +- Windows: установите Visual Studio Build Tools + +### Как сбросить сессию? + +Удалите папку `tdlib_data/`: +```bash +rm -rf tdlib_data/ +``` + +При следующем запуске потребуется заново авторизоваться. + +## Производительность + +### Приложение тормозит + +Проверьте: +1. Количество открытых чатов (лимит 200) +2. Количество сообщений в открытом чате (лимит 500) +3. Размер терминала (минимум 80x20) + +Приложение автоматически очищает старые данные при достижении лимитов. + +### Высокое использование памяти + +Это нормально при большом количестве чатов и сообщений. Приложение использует LRU кеши с ограничениями: +- 500 пользователей в кеше +- 500 сообщений на чат +- 200 чатов + +## Разработка + +### Как внести вклад в проект? + +См. [CONTRIBUTING.md](CONTRIBUTING.md) + +### Где найти план развития? + +См. [ROADMAP.md](ROADMAP.md) + +### Как сообщить о баге? + +Создайте issue на GitHub с описанием: +- Шаги для воспроизведения +- Ожидаемое и фактическое поведение +- Версия ОС и Rust +- Логи (если есть) + +## Безопасность + +### Безопасно ли хранить credentials в файле? + +Да, если вы: +1. Используете `~/.config/tele-tui/credentials` +2. Установили права доступа: `chmod 600 ~/.config/tele-tui/credentials` +3. Не коммитите этот файл в git (уже в `.gitignore`) + +### Что делать при компрометации credentials? + +1. Удалите приложение на https://my.telegram.org/apps +2. Создайте новое приложение с новыми credentials +3. Обновите файл `credentials` +4. Удалите папку `tdlib_data/` и авторизуйтесь заново + +### Включена ли двухфакторная аутентификация? + +Если вы включили 2FA в Telegram, приложение запросит пароль при первой авторизации. + +## Ещё вопросы? + +Создайте issue на GitHub или свяжитесь с maintainers. diff --git a/HOTKEYS.md b/HOTKEYS.md new file mode 100644 index 0000000..df67c01 --- /dev/null +++ b/HOTKEYS.md @@ -0,0 +1,144 @@ +# Горячие клавиши tele-tui + +## Общая навигация + +| Клавиша | Русская раскладка | Действие | +|---------|-------------------|----------| +| `↑` / `k` | `р` | Вверх по списку | +| `↓` / `j` | `о` | Вниз по списку | +| `Enter` | | Открыть чат / Отправить сообщение | +| `Esc` | | Закрыть чат / Отменить действие | +| `Ctrl+C` | | Выход из приложения | +| `Ctrl+R` | | Обновить список чатов | + +## Папки и поиск + +| Клавиша | Действие | +|---------|----------| +| `1-9` | Переключение между папками Telegram | +| `Ctrl+S` | Открыть поиск по чатам | +| `Ctrl+F` | Открыть поиск в текущем чате | +| `n` | Следующий результат поиска | +| `N` | Предыдущий результат поиска | + +## Работа с сообщениями + +### Навигация и выбор + +| Клавиша | Действие | +|---------|----------| +| `↑/↓` | Скролл сообщений (в открытом чате) | +| `↑` | Выбор сообщения (при пустом поле ввода) | +| `Esc` | Отменить выбор | + +### Действия с сообщениями + +| Клавиша | Русская раскладка | Действие | +|---------|-------------------|----------| +| `Enter` | | Редактировать выбранное сообщение | +| `r` | `к` | Ответить на сообщение (Reply) | +| `f` | `а` | Переслать сообщение (Forward) | +| `d` / `Delete` | `в` | Удалить сообщение | +| `y` | `н` | Копировать текст в буфер обмена | +| `e` | `у` | Добавить реакцию (Emoji picker) | +| `i` | | Открыть профиль чата/пользователя | + +## Модалки подтверждения + +### Удаление сообщения + +| Клавиша | Русская раскладка | Действие | +|---------|-------------------|----------| +| `y` / `Enter` | `н` | Подтвердить удаление | +| `n` / `Esc` | `т` | Отменить удаление | + +## Emoji Picker (реакции) + +| Клавиша | Действие | +|---------|----------| +| `←` | Влево по сетке эмодзи | +| `→` | Вправо по сетке эмодзи | +| `↑` | Вверх по сетке эмодзи | +| `↓` | Вниз по сетке эмодзи | +| `Enter` | Добавить/удалить реакцию | +| `Esc` | Закрыть emoji picker | + +## Редактирование текста + +### Навигация по тексту + +| Клавиша | Действие | +|---------|----------| +| `←` | Курсор влево | +| `→` | Курсор вправо | +| `Home` | Курсор в начало строки | +| `End` | Курсор в конец строки | + +### Редактирование + +| Клавиша | Действие | +|---------|----------| +| `Backspace` | Удалить символ слева от курсора | +| `Delete` | Удалить символ справа от курсора | +| `Enter` | Новая строка / Отправить (зависит от контекста) | + +## Режимы работы + +### Режим списка чатов +- Навигация: `↑/↓` +- Открыть чат: `Enter` +- Поиск: `Ctrl+S` +- Папки: `1-9` + +### Режим открытого чата +- Скролл: `↑/↓` +- Выбор сообщения: `↑` (при пустом инпуте) +- Поиск в чате: `Ctrl+F` +- Закрыть чат: `Esc` + +### Режим выбора сообщения +- Редактировать: `Enter` +- Ответить: `r` / `к` +- Переслать: `f` / `а` +- Удалить: `d` / `в` / `Delete` +- Копировать: `y` / `н` +- Реакция: `e` / `у` +- Отменить: `Esc` + +### Режим редактирования +- Редактировать текст: см. "Редактирование текста" +- Отправить: `Enter` +- Отменить: `Esc` + +### Режим ответа (Reply) +- Редактировать ответ: см. "Редактирование текста" +- Отправить: `Enter` +- Отменить: `Esc` + +### Режим пересылки (Forward) +- Выбрать чат: `↑/↓` +- Переслать: `Enter` +- Отменить: `Esc` + +## Поддержка русской раскладки + +Все основные vim-клавиши поддерживают русскую раскладку: + +| Английская | Русская | Действие | +|------------|---------|----------| +| `h` | `р` | Влево | +| `j` | `о` | Вниз | +| `k` | `л` | Вверх | +| `l` | `д` | Вправо | +| `r` | `к` | Reply | +| `f` | `а` | Forward | +| `d` | `в` | Delete | +| `y` | `н` | Copy (Yank) | +| `e` | `у` | Emoji reaction | + +## Подсказки + +- Текущие доступные команды всегда отображаются в нижней части экрана (footer) +- При открытой модалке доступны только действия этой модалки +- `Esc` всегда отменяет текущее действие и возвращает на шаг назад +- Блочный курсор █ показывает текущую позицию при редактировании текста diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..776834a --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,122 @@ +# Установка tele-tui + +## Требования + +- **Rust**: версия 1.70 или выше ([установить](https://rustup.rs/)) +- **TDLib**: скачивается автоматически через tdlib-rs + +## Шаг 1: Клонирование репозитория + +```bash +git clone https://github.com/your-username/tele-tui.git +cd tele-tui +``` + +## Шаг 2: Получение API credentials + +1. Перейдите на https://my.telegram.org/apps +2. Войдите с вашим номером телефона +3. Создайте новое приложение +4. Скопируйте **api_id** и **api_hash** + +## Шаг 3: Настройка credentials + +### Вариант A: XDG config directory (рекомендуется) + +Создайте файл `~/.config/tele-tui/credentials`: + +```bash +mkdir -p ~/.config/tele-tui +cat > ~/.config/tele-tui/credentials << EOF +API_ID=your_api_id_here +API_HASH=your_api_hash_here +EOF +``` + +### Вариант B: .env файл + +Создайте файл `.env` в корне проекта: + +```bash +cp credentials.example .env +# Отредактируйте .env и вставьте ваши credentials +``` + +## Шаг 4: Сборка + +```bash +cargo build --release +``` + +## Шаг 5: Запуск + +```bash +cargo run --release +``` + +Или запустите скомпилированный бинарник: + +```bash +./target/release/tele-tui +``` + +## Первый запуск + +При первом запуске вам нужно будет: + +1. Ввести номер телефона (с кодом страны, например: +79991234567) +2. Ввести код подтверждения из Telegram +3. Если включена 2FA — ввести пароль + +Сессия сохраняется в `./tdlib_data/`, при следующем запуске авторизация не потребуется. + +## Настройка (опционально) + +Конфигурационный файл создаётся автоматически при первом запуске в `~/.config/tele-tui/config.toml`. + +Вы можете отредактировать его для настройки: +- Часового пояса +- Цветовой схемы + +Пример конфигурации см. в файле `config.toml.example`. + +## Устранение неполадок + +### "Telegram API credentials not found!" + +Убедитесь, что вы создали файл credentials (см. Шаг 3). + +### "error: linking with `cc` failed" + +Убедитесь, что у вас установлен C компилятор: +- macOS: `xcode-select --install` +- Linux: `sudo apt-get install build-essential` (Debian/Ubuntu) +- Windows: установите Visual Studio Build Tools + +### TDLib download failed + +Проверьте подключение к интернету. TDLib скачивается автоматически при первой сборке. + +## Обновление + +```bash +git pull +cargo build --release +``` + +Ваши credentials и конфигурация сохранятся. + +## Удаление + +Чтобы полностью удалить приложение и все данные: + +```bash +# Удалить проект +rm -rf tele-tui/ + +# Удалить конфигурацию и credentials +rm -rf ~/.config/tele-tui/ + +# Удалить сессию Telegram (опционально, потребуется новая авторизация) +# rm -rf ./tdlib_data/ +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dadce1d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 tele-tui contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md new file mode 100644 index 0000000..93c94e6 --- /dev/null +++ b/PROJECT_STRUCTURE.md @@ -0,0 +1,355 @@ +# Структура проекта + +## Обзор директорий + +``` +tele-tui/ +├── .github/ # GitHub конфигурация +│ ├── ISSUE_TEMPLATE/ # Шаблоны для issue +│ │ ├── bug_report.md +│ │ └── feature_request.md +│ ├── workflows/ # GitHub Actions CI/CD +│ │ └── ci.yml +│ └── pull_request_template.md +│ +├── docs/ # Дополнительная документация +│ └── TDLIB_INTEGRATION.md +│ +├── src/ # Исходный код +│ ├── app/ # Состояние приложения +│ │ ├── mod.rs +│ │ └── state.rs +│ ├── input/ # Обработка пользовательского ввода +│ │ ├── mod.rs +│ │ ├── auth.rs +│ │ └── main_input.rs +│ ├── tdlib/ # TDLib интеграция +│ │ ├── mod.rs +│ │ └── client.rs +│ ├── ui/ # Рендеринг интерфейса +│ │ ├── mod.rs +│ │ ├── auth.rs +│ │ ├── chat_list.rs +│ │ ├── footer.rs +│ │ ├── loading.rs +│ │ ├── main_screen.rs +│ │ └── messages.rs +│ ├── config.rs # Конфигурация приложения +│ ├── main.rs # Точка входа +│ └── utils.rs # Утилиты +│ +├── tdlib_data/ # TDLib сессия (НЕ коммитится) +├── target/ # Артефакты сборки (НЕ коммитится) +│ +├── .editorconfig # EditorConfig для IDE +├── .gitignore # Git ignore правила +├── Cargo.lock # Зависимости (точные версии) +├── Cargo.toml # Манифест проекта +├── rustfmt.toml # Конфигурация форматирования +│ +├── config.toml.example # Пример конфигурации +├── credentials.example # Пример credentials +│ +├── CHANGELOG.md # История изменений +├── CLAUDE.md # Инструкции для Claude AI +├── CONTRIBUTING.md # Гайд по контрибуции +├── CONTEXT.md # Текущий статус разработки +├── DEVELOPMENT.md # Правила разработки +├── FAQ.md # Часто задаваемые вопросы +├── HOTKEYS.md # Список горячих клавиш +├── INSTALL.md # Инструкция по установке +├── LICENSE # MIT лицензия +├── PROJECT_STRUCTURE.md # Этот файл +├── README.md # Главная документация +├── REQUIREMENTS.md # Функциональные требования +├── ROADMAP.md # План развития +└── SECURITY.md # Политика безопасности +``` + +## Исходный код (src/) + +### main.rs +**Точка входа приложения** +- Инициализация TDLib клиента +- Event loop (60 FPS) +- Обработка Ctrl+C (graceful shutdown) +- Координация между UI, input и TDLib + +### config.rs +**Конфигурация приложения** +- Загрузка/сохранение TOML конфига +- Парсинг timezone и цветов +- Загрузка credentials (приоритетная система) +- XDG directory support + +### utils.rs +**Утилитарные функции** +- `disable_tdlib_logs()` — отключение TDLib логов через FFI +- `format_timestamp_with_tz()` — форматирование времени с учётом timezone +- `format_date()` — форматирование дат для разделителей +- `format_datetime()` — полное форматирование даты и времени +- `format_was_online()` — "был(а) X мин. назад" + +### app/ — Состояние приложения + +#### mod.rs +- `App` struct — главная структура состояния +- `needs_redraw` — флаг для оптимизации рендеринга +- Состояние модалок (delete confirm, reaction picker, profile) +- Состояние поиска и черновиков +- Методы для работы с UI state + +#### state.rs +- `AppScreen` enum — текущий экран (Loading, Auth, Main) + +### tdlib/ — Telegram интеграция + +#### client.rs +- `TdClient` — обёртка над TDLib +- Авторизация (телефон, код, 2FA) +- Загрузка чатов и сообщений +- Отправка/редактирование/удаление сообщений +- Reply, Forward +- Реакции (`ReactionInfo`) +- LRU кеши (users, statuses) +- `NetworkState` enum + +#### mod.rs +- Экспорт публичных типов + +### ui/ — Рендеринг интерфейса + +#### mod.rs +- `render()` — роутинг по экранам +- Проверка минимального размера терминала (80x20) + +#### loading.rs +- Экран "Loading..." + +#### auth.rs +- Экран авторизации (ввод телефона, кода, пароля) + +#### main_screen.rs +- Главный экран +- Отображение папок сверху + +#### chat_list.rs +- Список чатов +- Индикаторы: 📌, 🔇, @, (N) +- Онлайн-статус (●) +- Поиск по чатам + +#### messages.rs +- Область сообщений +- Группировка по дате и отправителю +- Markdown форматирование +- Реакции под сообщениями +- Emoji picker modal +- Profile modal +- Delete confirmation modal +- Pinned message +- Динамический инпут +- Блочный курсор + +#### footer.rs +- Футер с командами +- Индикатор состояния сети + +### input/ — Обработка ввода + +#### mod.rs +- Роутинг ввода по экранам + +#### auth.rs +- Обработка ввода на экране авторизации + +#### main_input.rs +- Обработка ввода на главном экране +- **Важно**: порядок обработчиков имеет значение! + 1. Reaction picker (Enter/Esc) + 2. Delete confirmation + 3. Profile modal + 4. Search в чате + 5. Forward mode + 6. Edit/Reply mode + 7. Message selection + 8. Chat list +- Поддержка русской раскладки + +## Конфигурационные файлы + +### Cargo.toml +Манифест проекта: +- Metadata (name, version, authors, license) +- Dependencies +- Build dependencies (tdlib-rs) + +### rustfmt.toml +Конфигурация `cargo fmt`: +- max_width = 100 +- imports_granularity = "Crate" +- Стиль комментариев + +### .editorconfig +Универсальные настройки для IDE: +- Unix line endings (LF) +- UTF-8 encoding +- Отступы (4 spaces для Rust) + +## Рантайм файлы + +### tdlib_data/ +Создаётся автоматически TDLib: +- Токены авторизации +- Кеш сообщений и файлов +- **НЕ коммитится** (в .gitignore) +- **НЕ делиться** (содержит чувствительные данные) + +### ~/.config/tele-tui/ +XDG config directory: +- `config.toml` — пользовательская конфигурация +- `credentials` — API_ID и API_HASH + +## Документация + +### Пользовательская +- **README.md** — главная страница, overview +- **INSTALL.md** — установка и настройка +- **HOTKEYS.md** — все горячие клавиши +- **FAQ.md** — часто задаваемые вопросы + +### Разработчика +- **CONTRIBUTING.md** — как внести вклад +- **DEVELOPMENT.md** — правила разработки +- **PROJECT_STRUCTURE.md** — этот файл +- **ROADMAP.md** — план развития +- **CONTEXT.md** — текущий статус, архитектурные решения + +### Спецификации +- **REQUIREMENTS.md** — функциональные требования +- **CHANGELOG.md** — история изменений +- **SECURITY.md** — политика безопасности + +### Внутренняя +- **CLAUDE.md** — инструкции для AI ассистента +- **docs/TDLIB_INTEGRATION.md** — детали интеграции TDLib + +## Ключевые концепции + +### Архитектура +- **Event-driven**: TDLib updates → mpsc channel → main loop +- **Unidirectional data flow**: TDLib → App state → UI rendering +- **Modal stacking**: приоритет обработки ввода для модалок + +### Оптимизации +- **needs_redraw**: рендеринг только при изменениях +- **LRU caches**: user_names, user_statuses (500 записей) +- **Limits**: 500 messages/chat, 200 chats +- **Lazy loading**: users загружаются батчами (5 за цикл) + +### Состояние +``` +App { + screen: AppScreen, + config: Config, + needs_redraw: bool, + + // TDLib state + chats: Vec, + folders: Vec, + + // UI state + selected_chat_id: Option, + input_text: String, + cursor_position: usize, + + // Modals + is_delete_confirmation: bool, + is_reaction_picker_mode: bool, + profile_info: Option, + + // Search + search_query: String, + search_results: Vec, + + // Drafts + drafts: HashMap, +} +``` + +## Потоки выполнения + +### Main thread +- Event loop (16ms tick для 60 FPS) +- UI rendering +- Input handling +- App state updates + +### TDLib thread +- `td_client.receive()` в отдельном Tokio task +- Updates отправляются через `mpsc::channel` +- Неблокирующий для main thread + +### Blocking operations +- Загрузка конфига (при запуске) +- Авторизация (блокирует до ввода кода) +- Graceful shutdown (2 sec timeout) + +## Зависимости + +### UI +- `ratatui` 0.29 — TUI framework +- `crossterm` 0.28 — terminal control + +### Telegram +- `tdlib-rs` 1.1 — TDLib bindings +- `tokio` 1.x — async runtime + +### Data +- `serde` + `serde_json` 1.0 — serialization +- `toml` 0.8 — config parsing +- `chrono` 0.4 — date/time + +### System +- `dirs` 5.0 — XDG directories +- `arboard` 3.4 — clipboard +- `open` 5.0 — открытие URL/файлов +- `dotenvy` 0.15 — .env файлы + +## Workflow разработки + +1. Изучить [ROADMAP.md](ROADMAP.md) — понять текущую фазу +2. Прочитать [DEVELOPMENT.md](DEVELOPMENT.md) — правила работы +3. Изучить [CONTEXT.md](CONTEXT.md) — архитектурные решения +4. Найти issue или создать новую фичу +5. Создать feature branch +6. Внести изменения +7. `cargo fmt` + `cargo clippy` +8. Протестировать вручную +9. Создать PR с описанием + +## CI/CD + +### GitHub Actions (.github/workflows/ci.yml) +- **Check**: `cargo check` +- **Format**: `cargo fmt --check` +- **Clippy**: `cargo clippy` +- **Build**: для Ubuntu, macOS, Windows + +Запускается на: +- Push в `main` или `develop` +- Pull requests + +## Безопасность + +### Чувствительные файлы (в .gitignore) +- `.env` +- `credentials` +- `config.toml` (если в корне проекта) +- `tdlib_data/` +- `target/` + +### Рекомендации +- Credentials в `~/.config/tele-tui/credentials` +- Права доступа: `chmod 600 ~/.config/tele-tui/credentials` +- Никогда не коммитить `tdlib_data/` diff --git a/README.md b/README.md index b766576..2f7365b 100644 --- a/README.md +++ b/README.md @@ -1 +1,163 @@ -telegram-tui консольный телеграм \ No newline at end of file +# tele-tui + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Rust](https://img.shields.io/badge/rust-1.70%2B-orange.svg)](https://www.rust-lang.org/) + +Консольный Telegram клиент с Vim-style навигацией. + +![tele-tui screenshot](docs/screenshot.png) + +## Возможности + +- **Полная интеграция с Telegram**: отправка/получение сообщений, редактирование, удаление, пересылка +- **Vim-style навигация**: hjkl + поддержка русской раскладки (ролд) +- **Markdown форматирование**: жирный, курсив, подчёркивание, зачёркивание, код, спойлеры, ссылки +- **Реакции на сообщения**: emoji picker с навигацией стрелками +- **Папки Telegram**: переключение между папками (1-9) +- **Поиск**: по чатам (Ctrl+S) и внутри чата (Ctrl+F) +- **Черновики**: автосохранение набранного текста при переключении чатов +- **Typing indicator**: показывает когда собеседник печатает +- **Закреплённые сообщения**: отображение и переход к закреплённому сообщению +- **Копирование в буфер**: copy сообщений в системный буфер обмена +- **Профиль**: просмотр информации о пользователе/чате +- **Конфигурация**: настройка цветов и часового пояса через TOML +- **Оптимизация**: 60 FPS, умное кеширование, graceful shutdown + +## Установка + +### Требования + +- Rust 1.70+ +- TDLib (скачивается автоматически через tdlib-rs) + +### Сборка + +```bash +git clone https://github.com/your-username/tele-tui.git +cd tele-tui +cargo build --release +``` + +### API Credentials + +Получите API credentials на https://my.telegram.org/apps + +Создайте файл `~/.config/tele-tui/credentials`: +``` +API_ID=your_api_id +API_HASH=your_api_hash +``` + +Или используйте `.env` файл в директории проекта: +``` +API_ID=your_api_id +API_HASH=your_api_hash +``` + +## Использование + +```bash +cargo run --release +``` + +При первом запуске нужно пройти авторизацию (телефон + код + опционально 2FA пароль). + +## Конфигурация + +Конфигурационный файл создаётся автоматически в `~/.config/tele-tui/config.toml`: + +```toml +[general] +# Часовой пояс в формате "+03:00" или "-05:00" +timezone = "+03:00" + +[colors] +# Поддерживаемые цвета: black, red, green, yellow, blue, magenta, cyan, gray, white, +# darkgray, lightred, lightgreen, lightyellow, lightblue, lightmagenta, lightcyan +incoming_message = "white" +outgoing_message = "green" +selected_message = "yellow" +reaction_chosen = "yellow" +reaction_other = "gray" +``` + +## Горячие клавиши + +### Навигация +- `↑/↓` или `k/j` (р/о) — навигация по списку чатов +- `Enter` — открыть чат / отправить сообщение +- `Esc` — закрыть чат / отменить действие +- `1-9` — переключение между папками +- `Ctrl+S` — поиск по чатам +- `Ctrl+R` — обновить список чатов +- `Ctrl+C` — выход + +### В открытом чате +- `↑/↓` — скролл сообщений +- `Ctrl+F` — поиск в чате +- `n/N` — следующий/предыдущий результат поиска +- `i` — информация о чате/пользователе + +### Работа с сообщениями +- `↑` при пустом инпуте — выбор сообщения +- `Enter` в режиме выбора — редактировать +- `r` / `к` — ответить (reply) +- `f` / `а` — переслать (forward) +- `d` / `в` / `Delete` — удалить +- `y` / `н` — скопировать в буфер +- `e` / `у` — добавить реакцию + +### Emoji Picker (реакции) +- `←/→/↑/↓` — навигация по сетке +- `Enter` — добавить/удалить реакцию +- `Esc` — закрыть picker + +### Редактирование текста +- `←/→` — перемещение курсора +- `Home` — в начало строки +- `End` — в конец строки +- `Backspace` — удалить символ слева +- `Delete` — удалить символ справа + +## Структура проекта + +``` +src/ +├── main.rs # Точка входа, event loop +├── config.rs # Конфигурация (TOML), credentials +├── app/ # Состояние приложения +├── ui/ # Отрисовка интерфейса +├── input/ # Обработка ввода +├── utils.rs # Утилиты (форматирование времени, логи) +└── tdlib/ # TDLib интеграция +``` + +## Зависимости + +- `ratatui` 0.29 — TUI framework +- `crossterm` 0.28 — terminal handling +- `tdlib-rs` 1.1 — Telegram API +- `tokio` 1.x — async runtime +- `serde` + `serde_json` — serialization +- `toml` 0.8 — config parsing +- `dirs` 5.0 — XDG directories +- `clipboard` 0.5 — clipboard access +- `chrono` 0.4 — date/time formatting + +## Документация + +- [INSTALL.md](INSTALL.md) — подробная инструкция по установке +- [HOTKEYS.md](HOTKEYS.md) — все горячие клавиши +- [FAQ.md](FAQ.md) — часто задаваемые вопросы +- [CONTRIBUTING.md](CONTRIBUTING.md) — как внести вклад +- [PROJECT_STRUCTURE.md](PROJECT_STRUCTURE.md) — структура проекта +- [SECURITY.md](SECURITY.md) — политика безопасности +- [CHANGELOG.md](CHANGELOG.md) — история изменений +- [REQUIREMENTS.md](REQUIREMENTS.md) — функциональные требования +- [DEVELOPMENT.md](DEVELOPMENT.md) — правила разработки +- [ROADMAP.md](ROADMAP.md) — план развития проекта +- [CONTEXT.md](CONTEXT.md) — текущий статус разработки + +## Лицензия + +MIT diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index 9e1278b..d423c71 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -55,6 +55,29 @@ 11) `@` — пинг/меншн. 12) с видео/картинками/голосовые пока ничего не делаем, ренденим заглушку (с упоминанием что это картинка или видео и тд) +### Дополнительно реализованные возможности + +13) **Markdown форматирование**: жирный, курсив, подчёркивание, зачёркивание, код, спойлеры, ссылки, упоминания +14) **Редактирование сообщений**: ↑ при пустом инпуте → выбор → Enter → редактирование +15) **Удаление сообщений**: d/в/Delete в режиме выбора → подтверждение → удаление +16) **Reply на сообщения**: r/к в режиме выбора → превью → отправка ответа +17) **Forward сообщений**: f/а в режиме выбора → выбор чата → пересылка +18) **Typing indicator**: отображение "печатает..." когда собеседник набирает текст +19) **Закреплённые сообщения**: отображение pinned message вверху чата с переходом +20) **Поиск по сообщениям**: Ctrl+F для поиска внутри чата, n/N для навигации +21) **Черновики**: автосохранение текста при переключении между чатами +22) **Профиль**: i для просмотра информации о пользователе/группе +23) **Копирование**: y/н для копирования сообщения в системный буфер +24) **Реакции**: e/у для добавления реакций, emoji picker с навигацией стрелками +25) **Конфигурация**: ~/.config/tele-tui/config.toml для настройки цветов и timezone +26) **Credentials**: приоритетная загрузка из ~/.config/tele-tui/credentials или .env +27) **Блочный курсор**: Vim-style курсор █ с навигацией ←/→/Home/End +28) **Динамический инпут**: автоматическое расширение до 10 строк +29) **Онлайн-статус**: зелёная точка ● для онлайн пользователей +30) **Индикаторы**: 📌 закреплённые чаты, 🔇 замьюченные, @ упоминания +31) **Состояние сети**: индикатор в футере (⚠ Нет сети, ⏳ Подключение...) +32) **Graceful shutdown**: корректное закрытие при Ctrl+C + ### Управление 1) ctrl+c или command+c - выход из программы 2) "h j k l" - влево, вниз, вверх, вправо (навигация в левом столбце) vim-style управление @@ -67,19 +90,38 @@ 9) поддержка русской раскладки: "р о л д" соответствует "h j k l" 10) Ctrl+R - обновить список чатов -### Реализованные команды (footer) +### Реализованные команды +#### Навигация ``` -j/k: Navigate | Ctrl+k: First | Enter: Open | Esc: Close | Ctrl+R: Refresh | Ctrl+C: Quit +↑/↓ или k/j (р/о): Navigate | Enter: Open/Send | Esc: Close/Cancel | 1-9: Folders +Ctrl+S: Search Chats | Ctrl+R: Refresh | Ctrl+F: Search in Chat | Ctrl+C: Quit +``` + +#### Работа с сообщениями +``` +↑ (пустой инпут): Select message | Enter: Edit | r/к: Reply | f/а: Forward +d/в/Delete: Delete | y/н: Copy | e/у: React | i: Profile +``` + +#### Emoji Picker (реакции) +``` +←/→/↑/↓: Navigate | Enter: Toggle reaction | Esc: Close ``` ## Технологии Пишем на rust-е -1) ratatui - для tui интерфейса -2) tdlib-rs - для подключения апи телеграма (обёртка над TDLib) -3) tokio - async runtime -4) crossterm - кроссплатформенный терминал +1) ratatui 0.29 - для tui интерфейса +2) tdlib-rs 1.1 - для подключения апи телеграма (обёртка над TDLib) +3) tokio 1.x - async runtime +4) crossterm 0.28 - кроссплатформенный терминал +5) serde + serde_json 1.0 - сериализация/десериализация +6) toml 0.8 - парсинг конфигурации +7) dirs 5.0 - XDG директории (config, data) +8) clipboard 0.5 - работа с системным буфером обмена +9) chrono 0.4 - форматирование даты/времени +10) dotenvy 0.15 - загрузка .env файлов ## Нефункциональные требования diff --git a/ROADMAP.md b/ROADMAP.md index 6c2caed..c1e6d0d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -112,7 +112,7 @@ - Esc для отмены - Отображение "↪ Переслано от" для пересланных сообщений -## Фаза 9: Расширенные возможности [IN PROGRESS] +## Фаза 9: Расширенные возможности [DONE] - [x] Typing indicator ("печатает...") - Показывать когда собеседник печатает @@ -139,7 +139,7 @@ - Отображение реакций под сообщениями - `e` в режиме выбора — добавить реакцию (emoji picker) - Список доступных реакций чата -- [ ] Конфигурационный файл +- [x] Конфигурационный файл - `~/.config/tele-tui/config.toml` - Настройки: цветовая схема, часовой пояс, хоткеи - Загрузка конфига при старте diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..8b8d966 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,64 @@ +# Security Policy + +## Поддерживаемые версии + +| Версия | Поддержка | +| ------ | ------------------ | +| 0.1.x | :white_check_mark: | + +## Сообщить об уязвимости + +Если вы обнаружили уязвимость в безопасности, пожалуйста: + +1. **НЕ создавайте публичный issue** +2. Отправьте email на: [security@example.com] +3. Опишите: + - Тип уязвимости + - Шаги для воспроизведения + - Потенциальное влияние + - Предложения по исправлению (если есть) + +## Безопасность credentials + +### Важно + +- **НИКОГДА** не коммитьте файлы с credentials в git +- Файлы `credentials` и `.env` уже добавлены в `.gitignore` +- TDLib сессия в `tdlib_data/` содержит токены авторизации — НЕ делитесь этой папкой + +### Рекомендации + +1. **Используйте XDG config directory**: + - Храните credentials в `~/.config/tele-tui/credentials` + - Установите права доступа: `chmod 600 ~/.config/tele-tui/credentials` + +2. **Защита сессии**: + - Папка `tdlib_data/` содержит вашу авторизованную сессию + - Не копируйте её на другие машины + - При компрометации — удалите папку и авторизуйтесь заново + +3. **Двухфакторная аутентификация**: + - Настоятельно рекомендуется включить 2FA в Telegram + - Это защитит ваш аккаунт даже при утечке API credentials + +## Известные ограничения + +### TDLib +- Приложение использует официальную библиотеку TDLib от Telegram +- Безопасность зависит от актуальности TDLib (автообновление через tdlib-rs) + +### Конфигурация +- Конфигурационный файл `config.toml` НЕ содержит чувствительных данных +- Credentials хранятся отдельно в файле `credentials` + +## Обновления безопасности + +Мы оперативно реагируем на сообщения об уязвимостях и выпускаем патчи в течение: +- **Критические**: 24-48 часов +- **Высокие**: 3-7 дней +- **Средние**: 2-4 недели +- **Низкие**: включаются в следующий релиз + +## Спасибо + +Мы ценим ваш вклад в безопасность проекта! diff --git a/config.toml.example b/config.toml.example new file mode 100644 index 0000000..000f35c --- /dev/null +++ b/config.toml.example @@ -0,0 +1,31 @@ +# tele-tui configuration file example +# +# Этот файл автоматически создаётся при первом запуске в ~/.config/tele-tui/config.toml +# Скопируйте его туда и настройте по своему усмотрению + +[general] +# Часовой пояс в формате "+03:00" или "-05:00" +# Применяется к отображению времени сообщений +timezone = "+03:00" + +[colors] +# Цветовая схема интерфейса +# Поддерживаемые цвета: +# - Основные: black, red, green, yellow, blue, magenta, cyan, gray, white +# - Светлые: lightred, lightgreen, lightyellow, lightblue, lightmagenta, lightcyan +# - Тёмные: darkgray + +# Цвет входящих сообщений +incoming_message = "white" + +# Цвет исходящих сообщений +outgoing_message = "green" + +# Цвет выбранного сообщения +selected_message = "yellow" + +# Цвет своих реакций (отображаются в рамках [👍]) +reaction_chosen = "yellow" + +# Цвет чужих реакций +reaction_other = "gray" diff --git a/credentials.example b/credentials.example new file mode 100644 index 0000000..d037160 --- /dev/null +++ b/credentials.example @@ -0,0 +1,10 @@ +# Telegram API Credentials +# +# Получите эти данные на https://my.telegram.org/apps +# Создайте приложение и скопируйте api_id и api_hash +# +# Этот файл должен быть размещён в ~/.config/tele-tui/credentials +# Альтернативно можно использовать .env файл в директории проекта + +API_ID=your_api_id_here +API_HASH=your_api_hash_here diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..1283a72 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,21 @@ +# Rustfmt configuration for tele-tui +# https://rust-lang.github.io/rustfmt/ + +edition = "2021" +max_width = 100 +tab_spaces = 4 +newline_style = "Unix" + +# Imports +imports_granularity = "Crate" +group_imports = "StdExternalCrate" + +# Comments +wrap_comments = true +comment_width = 80 +normalize_comments = true + +# Formatting +use_small_heuristics = "Default" +fn_call_width = 80 +struct_lit_width = 50 diff --git a/src/app/mod.rs b/src/app/mod.rs index c721345..fa48e81 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -7,6 +7,7 @@ use crate::tdlib::client::ChatInfo; use crate::tdlib::TdClient; pub struct App { + pub config: crate::config::Config, pub screen: AppScreen, pub td_client: TdClient, // Auth state @@ -88,11 +89,12 @@ pub struct App { impl App { - pub fn new() -> App { + pub fn new(config: crate::config::Config) -> App { let mut state = ListState::default(); state.select(Some(0)); App { + config, screen: AppScreen::Loading, td_client: TdClient::new(), phone_input: String::new(), diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..118d266 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,265 @@ +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Config { + #[serde(default)] + pub general: GeneralConfig, + #[serde(default)] + pub colors: ColorsConfig, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GeneralConfig { + /// Часовой пояс в формате "+03:00" или "-05:00" + #[serde(default = "default_timezone")] + pub timezone: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ColorsConfig { + /// Цвет входящих сообщений (white, gray, cyan и т.д.) + #[serde(default = "default_incoming_color")] + pub incoming_message: String, + + /// Цвет исходящих сообщений + #[serde(default = "default_outgoing_color")] + pub outgoing_message: String, + + /// Цвет выбранного сообщения + #[serde(default = "default_selected_color")] + pub selected_message: String, + + /// Цвет своих реакций + #[serde(default = "default_reaction_chosen_color")] + pub reaction_chosen: String, + + /// Цвет чужих реакций + #[serde(default = "default_reaction_other_color")] + pub reaction_other: String, +} + +// Дефолтные значения +fn default_timezone() -> String { + "+03:00".to_string() +} + +fn default_incoming_color() -> String { + "white".to_string() +} + +fn default_outgoing_color() -> String { + "green".to_string() +} + +fn default_selected_color() -> String { + "yellow".to_string() +} + +fn default_reaction_chosen_color() -> String { + "yellow".to_string() +} + +fn default_reaction_other_color() -> String { + "gray".to_string() +} + +impl Default for GeneralConfig { + fn default() -> Self { + Self { + timezone: default_timezone(), + } + } +} + +impl Default for ColorsConfig { + fn default() -> Self { + Self { + incoming_message: default_incoming_color(), + outgoing_message: default_outgoing_color(), + selected_message: default_selected_color(), + reaction_chosen: default_reaction_chosen_color(), + reaction_other: default_reaction_other_color(), + } + } +} + +impl Default for Config { + fn default() -> Self { + Self { + general: GeneralConfig::default(), + colors: ColorsConfig::default(), + } + } +} + +impl Config { + /// Путь к конфигурационному файлу + pub fn config_path() -> Option { + dirs::config_dir().map(|mut path| { + path.push("tele-tui"); + path.push("config.toml"); + path + }) + } + + /// Путь к директории конфигурации + pub fn config_dir() -> Option { + dirs::config_dir().map(|mut path| { + path.push("tele-tui"); + path + }) + } + + /// Загрузить конфигурацию из файла + pub fn load() -> Self { + let config_path = match Self::config_path() { + Some(path) => path, + None => { + eprintln!("Warning: Could not determine config directory, using defaults"); + return Self::default(); + } + }; + + if !config_path.exists() { + // Создаём дефолтный конфиг при первом запуске + let default_config = Self::default(); + if let Err(e) = default_config.save() { + eprintln!("Warning: Could not create default config: {}", e); + } + return default_config; + } + + match fs::read_to_string(&config_path) { + Ok(content) => { + match toml::from_str::(&content) { + Ok(config) => config, + Err(e) => { + eprintln!("Warning: Could not parse config file: {}", e); + Self::default() + } + } + } + Err(e) => { + eprintln!("Warning: Could not read config file: {}", e); + Self::default() + } + } + } + + /// Сохранить конфигурацию в файл + pub fn save(&self) -> Result<(), String> { + let config_dir = Self::config_dir() + .ok_or_else(|| "Could not determine config directory".to_string())?; + + // Создаём директорию если её нет + fs::create_dir_all(&config_dir) + .map_err(|e| format!("Could not create config directory: {}", e))?; + + let config_path = config_dir.join("config.toml"); + + let toml_string = toml::to_string_pretty(self) + .map_err(|e| format!("Could not serialize config: {}", e))?; + + fs::write(&config_path, toml_string) + .map_err(|e| format!("Could not write config file: {}", e))?; + + Ok(()) + } + + /// Парсит строку цвета в ratatui::style::Color + pub fn parse_color(&self, color_str: &str) -> ratatui::style::Color { + use ratatui::style::Color; + + match color_str.to_lowercase().as_str() { + "black" => Color::Black, + "red" => Color::Red, + "green" => Color::Green, + "yellow" => Color::Yellow, + "blue" => Color::Blue, + "magenta" => Color::Magenta, + "cyan" => Color::Cyan, + "gray" | "grey" => Color::Gray, + "white" => Color::White, + "darkgray" | "darkgrey" => Color::DarkGray, + "lightred" => Color::LightRed, + "lightgreen" => Color::LightGreen, + "lightyellow" => Color::LightYellow, + "lightblue" => Color::LightBlue, + "lightmagenta" => Color::LightMagenta, + "lightcyan" => Color::LightCyan, + _ => Color::White, // fallback + } + } + + /// Путь к файлу credentials + pub fn credentials_path() -> Option { + Self::config_dir().map(|dir| dir.join("credentials")) + } + + /// Загружает API_ID и API_HASH из credentials файла или .env + /// Возвращает (api_id, api_hash) или ошибку с инструкциями + pub fn load_credentials() -> Result<(i32, String), String> { + use std::env; + + // 1. Пробуем загрузить из ~/.config/tele-tui/credentials + if let Some(cred_path) = Self::credentials_path() { + if cred_path.exists() { + if let Ok(content) = fs::read_to_string(&cred_path) { + let mut api_id: Option = None; + let mut api_hash: Option = None; + + for line in content.lines() { + let line = line.trim(); + if line.is_empty() || line.starts_with('#') { + continue; + } + + if let Some((key, value)) = line.split_once('=') { + let key = key.trim(); + let value = value.trim(); + + match key { + "API_ID" => { + api_id = value.parse().ok(); + } + "API_HASH" => { + api_hash = Some(value.to_string()); + } + _ => {} + } + } + } + + if let (Some(id), Some(hash)) = (api_id, api_hash) { + return Ok((id, hash)); + } + } + } + } + + // 2. Пробуем загрузить из переменных окружения (.env) + if let (Ok(api_id_str), Ok(api_hash)) = (env::var("API_ID"), env::var("API_HASH")) { + if let Ok(api_id) = api_id_str.parse::() { + return Ok((api_id, api_hash)); + } + } + + // 3. Не нашли credentials - возвращаем инструкции + let credentials_path = Self::credentials_path() + .map(|p| p.display().to_string()) + .unwrap_or_else(|| "~/.config/tele-tui/credentials".to_string()); + + Err(format!( + "Telegram API credentials not found!\n\n\ + Please create a file at:\n {}\n\n\ + With the following content:\n\ + API_ID=your_api_id\n\ + API_HASH=your_api_hash\n\n\ + You can get API credentials at: https://my.telegram.org/apps\n\n\ + Alternatively, you can create a .env file in the current directory.", + credentials_path + )) + } +} diff --git a/src/main.rs b/src/main.rs index 3324c0c..3f595e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod app; +mod config; mod input; mod tdlib; mod ui; @@ -26,6 +27,9 @@ async fn main() -> Result<(), io::Error> { // Загружаем переменные окружения из .env let _ = dotenvy::dotenv(); + // Загружаем конфигурацию (создаёт дефолтный если отсутствует) + let config = config::Config::load(); + // Отключаем логи TDLib ДО создания клиента disable_tdlib_logs(); @@ -37,7 +41,7 @@ async fn main() -> Result<(), io::Error> { let mut terminal = Terminal::new(backend)?; // Create app state - let mut app = App::new(); + let mut app = App::new(config); let res = run_app(&mut terminal, &mut app).await; // Restore terminal diff --git a/src/tdlib/client.rs b/src/tdlib/client.rs index 6c5b8cd..2acbce0 100644 --- a/src/tdlib/client.rs +++ b/src/tdlib/client.rs @@ -264,11 +264,16 @@ pub struct TdClient { #[allow(dead_code)] impl TdClient { pub fn new() -> Self { - let api_id: i32 = env::var("API_ID") - .unwrap_or_else(|_| "0".to_string()) - .parse() - .unwrap_or(0); - let api_hash = env::var("API_HASH").unwrap_or_default(); + // Загружаем credentials из ~/.config/tele-tui/credentials или .env + let (api_id, api_hash) = match crate::config::Config::load_credentials() { + Ok(creds) => creds, + Err(err_msg) => { + eprintln!("\n{}\n", err_msg); + // Используем дефолтные значения, чтобы приложение запустилось + // Пользователь увидит сообщение об ошибке в UI + (0, String::new()) + } + }; let client_id = tdlib_rs::create_client(); diff --git a/src/ui/messages.rs b/src/ui/messages.rs index 679a487..cf165e0 100644 --- a/src/ui/messages.rs +++ b/src/ui/messages.rs @@ -6,7 +6,7 @@ use ratatui::{ Frame, }; use crate::app::App; -use crate::utils::{format_timestamp, format_date, get_day}; +use crate::utils::{format_timestamp_with_tz, format_date, get_day}; use tdlib_rs::enums::TextEntityType; use tdlib_rs::types::TextEntity; @@ -510,16 +510,16 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) { last_sender = Some(current_sender); } - // Форматируем время (HH:MM) - let time = format_timestamp(msg.date); + // Форматируем время (HH:MM) с учётом timezone из config + let time = format_timestamp_with_tz(msg.date, &app.config.general.timezone); - // Цвет сообщения (жёлтый если выбрано) + // Цвет сообщения (из config или жёлтый если выбрано) let msg_color = if is_selected { - Color::Yellow + app.config.parse_color(&app.config.colors.selected_message) } else if msg.is_outgoing { - Color::Green + app.config.parse_color(&app.config.colors.outgoing_message) } else { - Color::White + app.config.parse_color(&app.config.colors.incoming_message) }; // Маркер выбора @@ -694,9 +694,9 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) { }; let style = if reaction.is_chosen { - Style::default().fg(Color::Yellow) + Style::default().fg(app.config.parse_color(&app.config.colors.reaction_chosen)) } else { - Style::default().fg(Color::Gray) + Style::default().fg(app.config.parse_color(&app.config.colors.reaction_other)) }; reaction_spans.push(Span::styled(reaction_text, style)); diff --git a/src/utils.rs b/src/utils.rs index dd10a00..076fc50 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -22,20 +22,42 @@ pub fn disable_tdlib_logs() { } } -/// Форматирование timestamp в время HH:MM -pub fn format_timestamp(timestamp: i32) -> String { +/// Форматирование timestamp в время HH:MM с учётом timezone offset +/// timezone_str: строка формата "+03:00" или "-05:00" +pub fn format_timestamp_with_tz(timestamp: i32, timezone_str: &str) -> String { let secs = timestamp as i64; - // Конвертируем в локальное время (простой способ без chrono) - // UTC + смещение для локального времени - let hours = ((secs % 86400) / 3600) as u32; + + // Парсим timezone offset (например "+03:00" -> 3 часа) + let offset_hours = parse_timezone_offset(timezone_str); + + let hours = ((secs % 86400) / 3600) as i32; let minutes = ((secs % 3600) / 60) as u32; - // Примерное локальное время (добавим 3 часа для MSK, можно настроить) - let local_hours = (hours + 3) % 24; + // Применяем timezone offset + let local_hours = ((hours + offset_hours) % 24 + 24) % 24; format!("{:02}:{:02}", local_hours, minutes) } +/// Парсит timezone строку типа "+03:00" в количество часов +fn parse_timezone_offset(tz: &str) -> i32 { + // Простой парсинг "+03:00" или "-05:00" + if tz.len() >= 3 { + let sign = if tz.starts_with('-') { -1 } else { 1 }; + let hours_str = &tz[1..3]; + if let Ok(hours) = hours_str.parse::() { + return sign * hours; + } + } + 3 // fallback к MSK +} + +/// Устаревшая функция для обратной совместимости (используется дефолтный +03:00) +#[allow(dead_code)] +pub fn format_timestamp(timestamp: i32) -> String { + format_timestamp_with_tz(timestamp, "+03:00") +} + /// Форматирование timestamp в дату для разделителя pub fn format_date(timestamp: i32) -> String { use std::time::{SystemTime, UNIX_EPOCH};