fixes
Some checks are pending
CI / Check (pull_request) Waiting to run
CI / Format (pull_request) Waiting to run
CI / Clippy (pull_request) Waiting to run
CI / Build (macos-latest) (pull_request) Waiting to run
CI / Build (ubuntu-latest) (pull_request) Waiting to run
CI / Build (windows-latest) (pull_request) Waiting to run
Some checks are pending
CI / Check (pull_request) Waiting to run
CI / Format (pull_request) Waiting to run
CI / Clippy (pull_request) Waiting to run
CI / Build (macos-latest) (pull_request) Waiting to run
CI / Build (ubuntu-latest) (pull_request) Waiting to run
CI / Build (windows-latest) (pull_request) Waiting to run
This commit is contained in:
37
.editorconfig
Normal file
37
.editorconfig
Normal file
@@ -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
|
||||||
40
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
40
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -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]
|
||||||
|
|
||||||
|
## Логи
|
||||||
|
Если есть логи или сообщения об ошибках, вставьте их сюда:
|
||||||
|
```
|
||||||
|
вставьте логи здесь
|
||||||
|
```
|
||||||
|
|
||||||
|
## Дополнительный контекст
|
||||||
|
Любая другая информация, которая может помочь в решении проблемы.
|
||||||
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -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) и этой функции там нет
|
||||||
|
|
||||||
|
## Дополнительный контекст
|
||||||
|
Скриншоты, ссылки на похожие реализации в других приложениях, и т.д.
|
||||||
51
.github/pull_request_template.md
vendored
Normal file
51
.github/pull_request_template.md
vendored
Normal file
@@ -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 изменений.
|
||||||
|
|
||||||
|
## Дополнительные заметки
|
||||||
|
|
||||||
|
Любая дополнительная информация для ревьюверов.
|
||||||
50
.github/workflows/ci.yml
vendored
Normal file
50
.github/workflows/ci.yml
vendored
Normal file
@@ -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
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -6,3 +6,7 @@
|
|||||||
# Environment variables (contains API keys)
|
# Environment variables (contains API keys)
|
||||||
.env
|
.env
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Local config files (if created in project root)
|
||||||
|
config.toml
|
||||||
|
credentials
|
||||||
|
|||||||
66
CHANGELOG.md
Normal file
66
CHANGELOG.md
Normal file
@@ -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
|
||||||
67
CONTEXT.md
67
CONTEXT.md
@@ -1,6 +1,6 @@
|
|||||||
# Текущий контекст проекта
|
# Текущий контекст проекта
|
||||||
|
|
||||||
## Статус: Фаза 9 — Расширенные возможности
|
## Статус: Фаза 9 — ЗАВЕРШЕНО
|
||||||
|
|
||||||
### Что сделано
|
### Что сделано
|
||||||
|
|
||||||
@@ -51,6 +51,12 @@
|
|||||||
- Emoji picker с сеткой доступных реакций (8 в ряду)
|
- Emoji picker с сеткой доступных реакций (8 в ряду)
|
||||||
- Добавление/удаление реакций (toggle)
|
- Добавление/удаление реакций (toggle)
|
||||||
- Обновление реакций в реальном времени через Update::MessageInteractionInfo
|
- Обновление реакций в реальном времени через 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
|
- **Кеширование имён пользователей**: имена загружаются асинхронно и обновляются в UI
|
||||||
- **Папки Telegram**: загрузка и переключение между папками (1-9)
|
- **Папки Telegram**: загрузка и переключение между папками (1-9)
|
||||||
- **Медиа-заглушки**: [Фото], [Видео], [Голосовое], [Стикер], [GIF] и др.
|
- **Медиа-заглушки**: [Фото], [Видео], [Голосовое], [Стикер], [GIF] и др.
|
||||||
@@ -121,6 +127,7 @@
|
|||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
├── main.rs # Точка входа, event loop, TDLib инициализация, graceful shutdown
|
├── main.rs # Точка входа, event loop, TDLib инициализация, graceful shutdown
|
||||||
|
├── config.rs # Конфигурация (TOML), загрузка credentials
|
||||||
├── app/
|
├── app/
|
||||||
│ ├── mod.rs # App структура и состояние (needs_redraw флаг)
|
│ ├── mod.rs # App структура и состояние (needs_redraw флаг)
|
||||||
│ └── state.rs # AppScreen enum
|
│ └── state.rs # AppScreen enum
|
||||||
@@ -136,10 +143,10 @@ src/
|
|||||||
│ ├── mod.rs # Роутинг ввода
|
│ ├── mod.rs # Роутинг ввода
|
||||||
│ ├── auth.rs # Обработка ввода на экране авторизации
|
│ ├── auth.rs # Обработка ввода на экране авторизации
|
||||||
│ └── main_input.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/
|
└── tdlib/
|
||||||
├── mod.rs # Модуль экспорта (TdClient, UserOnlineStatus, NetworkState)
|
├── mod.rs # Модуль экспорта (TdClient, UserOnlineStatus, NetworkState)
|
||||||
└── client.rs # TdClient: авторизация, чаты, сообщения, кеш, NetworkState
|
└── client.rs # TdClient: авторизация, чаты, сообщения, кеш, NetworkState, ReactionInfo
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ключевые решения
|
### Ключевые решения
|
||||||
@@ -162,6 +169,10 @@ src/
|
|||||||
|
|
||||||
9. **Перенос текста**: Длинные сообщения автоматически разбиваются на строки с учётом ширины терминала. Для исходящих — time_mark на последней строке, для входящих — время на первой строке с отступом для остальных.
|
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<ReactionInfo>` для каждого сообщения. Обновляются в реальном времени через `Update::MessageInteractionInfo`. Emoji picker использует сетку 8x6 с навигацией стрелками. Приоритет обработки ввода: reaction picker → delete confirmation → остальные модалки (важно для корректной работы Enter/Esc).
|
||||||
|
|
||||||
### Зависимости (Cargo.toml)
|
### Зависимости (Cargo.toml)
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
@@ -173,20 +184,62 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
chrono = "0.4"
|
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_ID=your_api_id
|
||||||
API_HASH=your_api_hash
|
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. При первом запуске нужно пройти авторизацию
|
1. При первом запуске нужно пройти авторизацию
|
||||||
2. Время отображается с фиксированным смещением +3 (MSK)
|
|
||||||
|
|||||||
125
CONTRIBUTING.md
Normal file
125
CONTRIBUTING.md
Normal file
@@ -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/)
|
||||||
|
- Добавляйте комментарии для сложной логики
|
||||||
|
|
||||||
|
### Структура коммитов
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>: <краткое описание>
|
||||||
|
|
||||||
|
<подробное описание (опционально)>
|
||||||
|
```
|
||||||
|
|
||||||
|
Типы:
|
||||||
|
- `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).
|
||||||
191
Cargo.lock
generated
191
Cargo.lock
generated
@@ -434,13 +434,34 @@ dependencies = [
|
|||||||
"subtle",
|
"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]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "6.0.0"
|
version = "6.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
@@ -451,7 +472,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"option-ext",
|
"option-ext",
|
||||||
"redox_users",
|
"redox_users 0.5.2",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1631,6 +1652,17 @@ dependencies = [
|
|||||||
"bitflags",
|
"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]]
|
[[package]]
|
||||||
name = "redox_users"
|
name = "redox_users"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -1639,7 +1671,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.17",
|
"getrandom 0.2.17",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror",
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1894,6 +1926,15 @@ dependencies = [
|
|||||||
"zmij",
|
"zmij",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -2117,7 +2158,7 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f98c960258301bee0758a669fbe12ad8a97c6e764d2f30c5426eea008eebf2d2"
|
checksum = "f98c960258301bee0758a669fbe12ad8a97c6e764d2f30c5426eea008eebf2d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs",
|
"dirs 6.0.0",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -2152,6 +2193,7 @@ dependencies = [
|
|||||||
"arboard",
|
"arboard",
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"dirs 5.0.1",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"open",
|
"open",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
@@ -2159,6 +2201,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tdlib-rs",
|
"tdlib-rs",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2174,13 +2217,33 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"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]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.18"
|
version = "2.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
@@ -2310,6 +2373,47 @@ dependencies = [
|
|||||||
"tokio",
|
"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]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -2648,6 +2752,15 @@ dependencies = [
|
|||||||
"windows-link",
|
"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]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -2684,6 +2797,21 @@ dependencies = [
|
|||||||
"windows-link",
|
"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]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2717,6 +2845,12 @@ dependencies = [
|
|||||||
"windows_x86_64_msvc 0.53.1",
|
"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]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2729,6 +2863,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2741,6 +2881,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2765,6 +2911,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2777,6 +2929,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
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]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2789,6 +2947,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
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]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2801,6 +2965,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
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]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2813,6 +2983,15 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.51.0"
|
version = "0.51.0"
|
||||||
@@ -2990,7 +3169,7 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"sha1",
|
"sha1",
|
||||||
"thiserror",
|
"thiserror 2.0.18",
|
||||||
"time",
|
"time",
|
||||||
"xz2",
|
"xz2",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
name = "tele-tui"
|
name = "tele-tui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
authors = ["Your Name <your.email@example.com>"]
|
||||||
|
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]
|
[dependencies]
|
||||||
ratatui = "0.29"
|
ratatui = "0.29"
|
||||||
@@ -14,6 +20,8 @@ dotenvy = "0.15"
|
|||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
open = "5.0"
|
open = "5.0"
|
||||||
arboard = "3.4"
|
arboard = "3.4"
|
||||||
|
toml = "0.8"
|
||||||
|
dirs = "5.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tdlib-rs = { version = "1.1", features = ["download-tdlib"] }
|
tdlib-rs = { version = "1.1", features = ["download-tdlib"] }
|
||||||
|
|||||||
227
FAQ.md
Normal file
227
FAQ.md
Normal file
@@ -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.
|
||||||
144
HOTKEYS.md
Normal file
144
HOTKEYS.md
Normal file
@@ -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` всегда отменяет текущее действие и возвращает на шаг назад
|
||||||
|
- Блочный курсор █ показывает текущую позицию при редактировании текста
|
||||||
122
INSTALL.md
Normal file
122
INSTALL.md
Normal file
@@ -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/
|
||||||
|
```
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -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.
|
||||||
355
PROJECT_STRUCTURE.md
Normal file
355
PROJECT_STRUCTURE.md
Normal file
@@ -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<Chat>,
|
||||||
|
folders: Vec<Folder>,
|
||||||
|
|
||||||
|
// UI state
|
||||||
|
selected_chat_id: Option<i64>,
|
||||||
|
input_text: String,
|
||||||
|
cursor_position: usize,
|
||||||
|
|
||||||
|
// Modals
|
||||||
|
is_delete_confirmation: bool,
|
||||||
|
is_reaction_picker_mode: bool,
|
||||||
|
profile_info: Option<ProfileInfo>,
|
||||||
|
|
||||||
|
// Search
|
||||||
|
search_query: String,
|
||||||
|
search_results: Vec<i64>,
|
||||||
|
|
||||||
|
// Drafts
|
||||||
|
drafts: HashMap<i64, String>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Потоки выполнения
|
||||||
|
|
||||||
|
### 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/`
|
||||||
164
README.md
164
README.md
@@ -1 +1,163 @@
|
|||||||
telegram-tui консольный телеграм
|
# tele-tui
|
||||||
|
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
[](https://www.rust-lang.org/)
|
||||||
|
|
||||||
|
Консольный Telegram клиент с Vim-style навигацией.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Возможности
|
||||||
|
|
||||||
|
- **Полная интеграция с 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
|
||||||
|
|||||||
@@ -55,6 +55,29 @@
|
|||||||
11) `@` — пинг/меншн.
|
11) `@` — пинг/меншн.
|
||||||
12) с видео/картинками/голосовые пока ничего не делаем, ренденим заглушку (с упоминанием что это картинка или видео и тд)
|
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 - выход из программы
|
1) ctrl+c или command+c - выход из программы
|
||||||
2) "h j k l" - влево, вниз, вверх, вправо (навигация в левом столбце) vim-style управление
|
2) "h j k l" - влево, вниз, вверх, вправо (навигация в левом столбце) vim-style управление
|
||||||
@@ -67,19 +90,38 @@
|
|||||||
9) поддержка русской раскладки: "р о л д" соответствует "h j k l"
|
9) поддержка русской раскладки: "р о л д" соответствует "h j k l"
|
||||||
10) Ctrl+R - обновить список чатов
|
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-е
|
Пишем на rust-е
|
||||||
|
|
||||||
1) ratatui - для tui интерфейса
|
1) ratatui 0.29 - для tui интерфейса
|
||||||
2) tdlib-rs - для подключения апи телеграма (обёртка над TDLib)
|
2) tdlib-rs 1.1 - для подключения апи телеграма (обёртка над TDLib)
|
||||||
3) tokio - async runtime
|
3) tokio 1.x - async runtime
|
||||||
4) crossterm - кроссплатформенный терминал
|
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 файлов
|
||||||
|
|
||||||
## Нефункциональные требования
|
## Нефункциональные требования
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@
|
|||||||
- Esc для отмены
|
- Esc для отмены
|
||||||
- Отображение "↪ Переслано от" для пересланных сообщений
|
- Отображение "↪ Переслано от" для пересланных сообщений
|
||||||
|
|
||||||
## Фаза 9: Расширенные возможности [IN PROGRESS]
|
## Фаза 9: Расширенные возможности [DONE]
|
||||||
|
|
||||||
- [x] Typing indicator ("печатает...")
|
- [x] Typing indicator ("печатает...")
|
||||||
- Показывать когда собеседник печатает
|
- Показывать когда собеседник печатает
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
- Отображение реакций под сообщениями
|
- Отображение реакций под сообщениями
|
||||||
- `e` в режиме выбора — добавить реакцию (emoji picker)
|
- `e` в режиме выбора — добавить реакцию (emoji picker)
|
||||||
- Список доступных реакций чата
|
- Список доступных реакций чата
|
||||||
- [ ] Конфигурационный файл
|
- [x] Конфигурационный файл
|
||||||
- `~/.config/tele-tui/config.toml`
|
- `~/.config/tele-tui/config.toml`
|
||||||
- Настройки: цветовая схема, часовой пояс, хоткеи
|
- Настройки: цветовая схема, часовой пояс, хоткеи
|
||||||
- Загрузка конфига при старте
|
- Загрузка конфига при старте
|
||||||
|
|||||||
64
SECURITY.md
Normal file
64
SECURITY.md
Normal file
@@ -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 недели
|
||||||
|
- **Низкие**: включаются в следующий релиз
|
||||||
|
|
||||||
|
## Спасибо
|
||||||
|
|
||||||
|
Мы ценим ваш вклад в безопасность проекта!
|
||||||
31
config.toml.example
Normal file
31
config.toml.example
Normal file
@@ -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"
|
||||||
10
credentials.example
Normal file
10
credentials.example
Normal file
@@ -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
|
||||||
21
rustfmt.toml
Normal file
21
rustfmt.toml
Normal file
@@ -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
|
||||||
@@ -7,6 +7,7 @@ use crate::tdlib::client::ChatInfo;
|
|||||||
use crate::tdlib::TdClient;
|
use crate::tdlib::TdClient;
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
|
pub config: crate::config::Config,
|
||||||
pub screen: AppScreen,
|
pub screen: AppScreen,
|
||||||
pub td_client: TdClient,
|
pub td_client: TdClient,
|
||||||
// Auth state
|
// Auth state
|
||||||
@@ -88,11 +89,12 @@ pub struct App {
|
|||||||
|
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new() -> App {
|
pub fn new(config: crate::config::Config) -> App {
|
||||||
let mut state = ListState::default();
|
let mut state = ListState::default();
|
||||||
state.select(Some(0));
|
state.select(Some(0));
|
||||||
|
|
||||||
App {
|
App {
|
||||||
|
config,
|
||||||
screen: AppScreen::Loading,
|
screen: AppScreen::Loading,
|
||||||
td_client: TdClient::new(),
|
td_client: TdClient::new(),
|
||||||
phone_input: String::new(),
|
phone_input: String::new(),
|
||||||
|
|||||||
265
src/config.rs
Normal file
265
src/config.rs
Normal file
@@ -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<PathBuf> {
|
||||||
|
dirs::config_dir().map(|mut path| {
|
||||||
|
path.push("tele-tui");
|
||||||
|
path.push("config.toml");
|
||||||
|
path
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Путь к директории конфигурации
|
||||||
|
pub fn config_dir() -> Option<PathBuf> {
|
||||||
|
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::<Config>(&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<PathBuf> {
|
||||||
|
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<i32> = None;
|
||||||
|
let mut api_hash: Option<String> = 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::<i32>() {
|
||||||
|
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
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
mod app;
|
mod app;
|
||||||
|
mod config;
|
||||||
mod input;
|
mod input;
|
||||||
mod tdlib;
|
mod tdlib;
|
||||||
mod ui;
|
mod ui;
|
||||||
@@ -26,6 +27,9 @@ async fn main() -> Result<(), io::Error> {
|
|||||||
// Загружаем переменные окружения из .env
|
// Загружаем переменные окружения из .env
|
||||||
let _ = dotenvy::dotenv();
|
let _ = dotenvy::dotenv();
|
||||||
|
|
||||||
|
// Загружаем конфигурацию (создаёт дефолтный если отсутствует)
|
||||||
|
let config = config::Config::load();
|
||||||
|
|
||||||
// Отключаем логи TDLib ДО создания клиента
|
// Отключаем логи TDLib ДО создания клиента
|
||||||
disable_tdlib_logs();
|
disable_tdlib_logs();
|
||||||
|
|
||||||
@@ -37,7 +41,7 @@ async fn main() -> Result<(), io::Error> {
|
|||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
// Create app state
|
// Create app state
|
||||||
let mut app = App::new();
|
let mut app = App::new(config);
|
||||||
let res = run_app(&mut terminal, &mut app).await;
|
let res = run_app(&mut terminal, &mut app).await;
|
||||||
|
|
||||||
// Restore terminal
|
// Restore terminal
|
||||||
|
|||||||
@@ -264,11 +264,16 @@ pub struct TdClient {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl TdClient {
|
impl TdClient {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let api_id: i32 = env::var("API_ID")
|
// Загружаем credentials из ~/.config/tele-tui/credentials или .env
|
||||||
.unwrap_or_else(|_| "0".to_string())
|
let (api_id, api_hash) = match crate::config::Config::load_credentials() {
|
||||||
.parse()
|
Ok(creds) => creds,
|
||||||
.unwrap_or(0);
|
Err(err_msg) => {
|
||||||
let api_hash = env::var("API_HASH").unwrap_or_default();
|
eprintln!("\n{}\n", err_msg);
|
||||||
|
// Используем дефолтные значения, чтобы приложение запустилось
|
||||||
|
// Пользователь увидит сообщение об ошибке в UI
|
||||||
|
(0, String::new())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let client_id = tdlib_rs::create_client();
|
let client_id = tdlib_rs::create_client();
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use ratatui::{
|
|||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
use crate::app::App;
|
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::enums::TextEntityType;
|
||||||
use tdlib_rs::types::TextEntity;
|
use tdlib_rs::types::TextEntity;
|
||||||
|
|
||||||
@@ -510,16 +510,16 @@ pub fn render(f: &mut Frame, area: Rect, app: &App) {
|
|||||||
last_sender = Some(current_sender);
|
last_sender = Some(current_sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Форматируем время (HH:MM)
|
// Форматируем время (HH:MM) с учётом timezone из config
|
||||||
let time = format_timestamp(msg.date);
|
let time = format_timestamp_with_tz(msg.date, &app.config.general.timezone);
|
||||||
|
|
||||||
// Цвет сообщения (жёлтый если выбрано)
|
// Цвет сообщения (из config или жёлтый если выбрано)
|
||||||
let msg_color = if is_selected {
|
let msg_color = if is_selected {
|
||||||
Color::Yellow
|
app.config.parse_color(&app.config.colors.selected_message)
|
||||||
} else if msg.is_outgoing {
|
} else if msg.is_outgoing {
|
||||||
Color::Green
|
app.config.parse_color(&app.config.colors.outgoing_message)
|
||||||
} else {
|
} 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 {
|
let style = if reaction.is_chosen {
|
||||||
Style::default().fg(Color::Yellow)
|
Style::default().fg(app.config.parse_color(&app.config.colors.reaction_chosen))
|
||||||
} else {
|
} 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));
|
reaction_spans.push(Span::styled(reaction_text, style));
|
||||||
|
|||||||
36
src/utils.rs
36
src/utils.rs
@@ -22,20 +22,42 @@ pub fn disable_tdlib_logs() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Форматирование timestamp в время HH:MM
|
/// Форматирование timestamp в время HH:MM с учётом timezone offset
|
||||||
pub fn format_timestamp(timestamp: i32) -> String {
|
/// timezone_str: строка формата "+03:00" или "-05:00"
|
||||||
|
pub fn format_timestamp_with_tz(timestamp: i32, timezone_str: &str) -> String {
|
||||||
let secs = timestamp as i64;
|
let secs = timestamp as i64;
|
||||||
// Конвертируем в локальное время (простой способ без chrono)
|
|
||||||
// UTC + смещение для локального времени
|
// Парсим timezone offset (например "+03:00" -> 3 часа)
|
||||||
let hours = ((secs % 86400) / 3600) as u32;
|
let offset_hours = parse_timezone_offset(timezone_str);
|
||||||
|
|
||||||
|
let hours = ((secs % 86400) / 3600) as i32;
|
||||||
let minutes = ((secs % 3600) / 60) as u32;
|
let minutes = ((secs % 3600) / 60) as u32;
|
||||||
|
|
||||||
// Примерное локальное время (добавим 3 часа для MSK, можно настроить)
|
// Применяем timezone offset
|
||||||
let local_hours = (hours + 3) % 24;
|
let local_hours = ((hours + offset_hours) % 24 + 24) % 24;
|
||||||
|
|
||||||
format!("{:02}:{:02}", local_hours, minutes)
|
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::<i32>() {
|
||||||
|
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 в дату для разделителя
|
/// Форматирование timestamp в дату для разделителя
|
||||||
pub fn format_date(timestamp: i32) -> String {
|
pub fn format_date(timestamp: i32) -> String {
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|||||||
Reference in New Issue
Block a user