@emaxe/tuigram 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example ADDED
@@ -0,0 +1,29 @@
1
+ # ── Telegram API Credentials ──────────────────────────────────────────────────
2
+ # Получить на https://my.telegram.org -> API development tools.
3
+ # Проще всего заполнить командой: tuigram init
4
+ TELEGRAM_API_ID=
5
+ TELEGRAM_API_HASH=
6
+
7
+ # ── Data & Storage Directories ────────────────────────────────────────────────
8
+ # По умолчанию файлы лежат в пользовательских директориях ОС и НЕ внутри пакета:
9
+ # настройки (этот файл): ~/.config/tuigram/.env
10
+ # %APPDATA%\tuigram\.env (Windows)
11
+ # сессия и загрузки: ~/.local/share/tuigram/
12
+ # %LOCALAPPDATA%\tuigram\ (Windows)
13
+ # Переопределить можно переменными ниже (раскомментируйте при необходимости).
14
+ # TUIGRAM_CONFIG_DIR=
15
+ # TUIGRAM_DATA_DIR=
16
+
17
+ # Устаревшая переменная: относительный путь разрешается от корня пакета.
18
+ # Оставлена для совместимости со старыми установками из клона репозитория.
19
+ # DATA_DIR=./data
20
+
21
+ # ── TUI UI Preferences ────────────────────────────────────────────────────────
22
+ # Тема интерфейса: default (тёмная), nord, light (светлая)
23
+ TUI_THEME=default
24
+
25
+ # Автоскролл к последнему сообщению при открытии чата (true/false)
26
+ AUTO_SCROLL=true
27
+
28
+ # Показывать уведомления о наборе текста (true/false)
29
+ SHOW_TYPING=true
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Maksim Klisin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,364 @@
1
+ # 🚀 TuiGram
2
+
3
+ [![npm](https://img.shields.io/npm/v/@emaxe/tuigram)](https://www.npmjs.com/package/@emaxe/tuigram)
4
+ [![node](https://img.shields.io/node/v/@emaxe/tuigram)](https://nodejs.org)
5
+ [![license](https://img.shields.io/npm/l/@emaxe/tuigram)](./LICENSE)
6
+
7
+ Полнофункциональный терминальный клиент Telegram (TUI + CLI) на Node.js на базе протокола MTProto (`teleproto`).
8
+
9
+ Позволяет полноценно общаться в Telegram прямо из терминала: просматривать список чатов с разделением по категориям, читать переписку с сохранением форматирования (жирный, курсив, ссылки, подсветка кода), отправлять сообщения, ответы (Reply), редактировать, удалять, ставить реакции и отправлять файлы.
10
+
11
+ ```
12
+ ┌─────────────────────────────────────────────────────────────────────────────────────────────┐
13
+ │ 🚀 TuiGram │ Maksim (@maksim) │ ● В сети │
14
+ │ 👥 Tech Chat [1420 уч.] ✍️ Alex печатает... │
15
+ ├──────────────────────────────┬──────────────────────────────────────────────────────────────┤
16
+ │ [1:Все] [2:ЛС] [3:Группы]... │ ─────── Сегодня ─────── │
17
+ │ [/] Поиск чатов... │ │
18
+ │ 📌 👤 Pavel Durov · Let me… │ Alex [13:40] │
19
+ │ 👥 Tech Chat · Nice work! [3]│ Have you checked the latest release? │
20
+ │ 🤖 BotFather · Done! 12:10 │ Вы [13:42] ✓✓ │
21
+ │ 📢 News Channel · Дайд… [12] │ ┌─ Ответ на сообщение #1042 │
22
+ │ │ Yes, testing it right now! │
23
+ │ │ 👍 4 🔥 2 │
24
+ │ ├──────────────────────────────────────────────────────────────┤
25
+ │ │ ↩️ Ответ на [Alex]: "Have you checked..." [Esc: Отмена] │
26
+ │ │ Пишу ответ прямо здесь█ │
27
+ │ │ │
28
+ ├──────────────────────────────┴──────────────────────────────────────────────────────────────┤
29
+ │ [Tab] Панель │ [Enter] Отправить │ [1-6] Вкладки │ [F1] Помощь │ [Ctrl+A] Действия │ [Ctrl+Q]│
30
+ └─────────────────────────────────────────────────────────────────────────────────────────────┘
31
+ ```
32
+
33
+ ---
34
+
35
+ ## ⚡ Особенности и возможности
36
+
37
+ - **Полноценный интерактивный TUI**:
38
+ - Двухпанельный адаптивный интерфейс (список чатов слева + история и поле ввода справа);
39
+ - Поддержка управления клавиатурой и мышью (клики, прокрутка колесом);
40
+ - Категории чатов: Все, Личные (ЛС), Группы, Каналы, Боты, Непрочитанные;
41
+ - Мгновенный поиск и фильтрация чатов по названию и `@username` (`/`);
42
+ - Отображение статуса набора текста («Собеседник печатает...») в реальном времени;
43
+ - Цветовое форматирование Telegram Entities (Bold, Italic, Monospace Code, URLs, Mentions, Spoilers);
44
+ - Индикаторы медиа-вложений (фото, видео, документы, голосовые, стикеры, опросы);
45
+ - Реакции на сообщения (👍, 🔥, ❤️);
46
+ - Бесконечная пагинация истории сообщений вверх (`PageUp` / `Ctrl+U`);
47
+ - Контекстные режимы быстрого ответа (Reply `Ctrl+R`) и редактирования (Edit `Ctrl+E`);
48
+ - Модальные окна: Справка (`F1` / `?`), Сведения о чате (`Ctrl+P`), Меню действий (`Ctrl+A`), Отправка файла (`Ctrl+O`).
49
+ - **Автономный CLI-режим**:
50
+ - Быстрая отправка сообщений и файлов из командной строки;
51
+ - Просмотр списка диалогов и истории в терминале;
52
+ - Потоковый стриминг живых обновлений.
53
+ - **Умная конфигурация**:
54
+ - Ключи и сессия хранятся в пользовательских директориях ОС — пакет остаётся read-only и переживает обновления;
55
+ - Безопасное хранение сессии и ключей (`chmod 0600`).
56
+
57
+ ---
58
+
59
+ ## 📦 Установка
60
+
61
+ ### Вариант 1: глобальная установка из npm (рекомендуется)
62
+
63
+ ```bash
64
+ npm install -g @emaxe/tuigram
65
+ ```
66
+
67
+ После этого команда `tuigram` доступна в любом каталоге.
68
+
69
+ ### Вариант 2: разовый запуск без установки
70
+
71
+ ```bash
72
+ npx @emaxe/tuigram
73
+ ```
74
+
75
+ ### Вариант 3: из исходников (для разработки)
76
+
77
+ ```bash
78
+ git clone https://github.com/emaxe/tuigram.git
79
+ cd tuigram
80
+ npm install
81
+ npm start
82
+ ```
83
+
84
+ ---
85
+
86
+ ## ⚙️ Первый запуск
87
+
88
+ ### 1. Ключи Telegram API
89
+
90
+ Получите `api_id` и `api_hash` на [https://my.telegram.org](https://my.telegram.org) (раздел *API development tools*), затем:
91
+
92
+ ```bash
93
+ tuigram init
94
+ ```
95
+
96
+ Команда спросит оба ключа и сохранит их с правами `0600`. Для скриптов и CI есть неинтерактивный вариант:
97
+
98
+ ```bash
99
+ tuigram init --api-id 1234567 --api-hash 0123456789abcdef0123456789abcdef
100
+ ```
101
+
102
+ Альтернатива — переменные окружения `TELEGRAM_API_ID` и `TELEGRAM_API_HASH`: они имеют приоритет над файлом настроек.
103
+
104
+ ### 2. Авторизация и запуск
105
+
106
+ ```bash
107
+ tuigram
108
+ ```
109
+
110
+ При первом запуске TuiGram предложит ввести номер телефона, код подтверждения из Telegram и пароль 2FA (если включён). После этого сессия сохранится, и последующие запуски будут происходить мгновенно.
111
+
112
+ Отдельно авторизоваться можно командой `tuigram login`.
113
+
114
+ > **В неинтерактивной среде** (CI, пайп, `< /dev/null`) вход по телефону невозможен:
115
+ > `tuigram login` сразу завершится с понятной ошибкой вместо зависания. Если сессия
116
+ > уже есть — команда просто сообщит, под кем вы авторизованы, и вернёт код `0`.
117
+ > Для автоматизации положите готовый `session.txt` в директорию данных
118
+ > (путь подскажет `tuigram paths`).
119
+
120
+ ---
121
+
122
+ ## 📂 Где хранятся файлы
123
+
124
+ Ничего не пишется внутрь самого пакета — это важно при глобальной установке, где каталог `node_modules` обычно недоступен на запись и стирается при обновлении.
125
+
126
+ | Что | macOS / Linux | Windows |
127
+ |---|---|---|
128
+ | Настройки (`.env`) | `~/.config/tuigram/.env` | `%APPDATA%\tuigram\.env` |
129
+ | Сессия | `~/.local/share/tuigram/session.txt` | `%LOCALAPPDATA%\tuigram\session.txt` |
130
+ | Загрузки из чатов | `~/.local/share/tuigram/downloads/` | `%LOCALAPPDATA%\tuigram\downloads\` |
131
+
132
+ Посмотреть актуальные пути и состояние конфигурации:
133
+
134
+ ```bash
135
+ tuigram paths
136
+ ```
137
+
138
+ Переопределить расположение можно переменными `TUIGRAM_CONFIG_DIR` и `TUIGRAM_DATA_DIR` (учитываются также `XDG_CONFIG_HOME` / `XDG_DATA_HOME`).
139
+
140
+ **Приоритет настроек** (сверху вниз, первое найденное выигрывает):
141
+ 1. переменные окружения процесса;
142
+ 2. `.env` в корне проекта — только при запуске из клона репозитория;
143
+ 3. `~/.config/tuigram/.env` — основной файл установленного CLI.
144
+
145
+ Сессия из старых установок (`<проект>/data/session.txt`) переносится в новое расположение автоматически при первом запуске — повторно логиниться не нужно.
146
+
147
+ ---
148
+
149
+ ## 🛠 Разработка
150
+
151
+ При запуске из клона репозитория работает интерактивное меню-диспетчер:
152
+
153
+ ```bash
154
+ ./run.sh # или npm run menu
155
+ ```
156
+
157
+ Оно даёт удобный выбор режима (TUI, логин, диалоги, отправка, тесты, очистка).
158
+
159
+ Тесты и проверка содержимого будущего npm-пакета:
160
+
161
+ ```bash
162
+ npm test # юнит-тесты
163
+ node scripts/check-package.js # проверка, что в пакет не утекают .env и сессия
164
+ ```
165
+
166
+ ---
167
+
168
+ ## ⌨️ Горячие клавиши
169
+
170
+ | Сочетание клавиш | Область | Действие |
171
+ |---|---|---|
172
+ | `Tab` / `Shift+Tab` | Глобально | Фокус по кругу: Список диалогов → Лента сообщений → Поле ввода |
173
+ | `↑` / `↓` | Список диалогов | Выбор чата |
174
+ | `Enter` | Список диалогов | Открыть выбранный чат и загрузить историю |
175
+ | `1` .. `6` | Список диалогов | Переключение категорий: `1:Все`, `2:ЛС`, `3:Группы`, `4:Каналы`, `5:Боты`, `6:Непроч` |
176
+ | `/` | Список диалогов | Поиск / фильтрация чатов |
177
+ | `Enter` | Поле ввода | Отправить набранное сообщение |
178
+ | `Ctrl+J` | Поле ввода | Перенос строки без отправки |
179
+ | `Ctrl+R` | Чат / Ввод | Ответить (Reply) на последнее сообщение |
180
+ | `Ctrl+E` | Чат / Ввод | Редактировать своё последнее сообщение |
181
+ | `Ctrl+A` | Сообщения | Контекстное меню действий (Реакции, Удаление, Скачивание, Ответ) |
182
+ | `Ctrl+O` | Глобально | Отправить файл / фото / документ |
183
+ | `Ctrl+F` | Окно отправки | Обзор файлов (навигация по папкам) |
184
+ | `Ctrl+D` | Окно отправки | Отправить без сжатия, документом |
185
+ | `Ctrl+P` | Глобально | Информация о текущем чате (ID, участники, ссылки) |
186
+ | `PageUp` / `Ctrl+U`| История | Прокрутка вверх / подгрузка старой истории |
187
+ | `PageDown` / `Ctrl+D`| История | Прокрутка вниз |
188
+ | `Esc` | Модальные окна | Закрыть модальное окно / отменить Reply/Edit |
189
+ | `F1` или `?` | Глобально | Окно справки со всеми горячими клавишами |
190
+ | `Ctrl+Q` / `Ctrl+C` | Глобально | Безопасный выход из клиента |
191
+
192
+ ---
193
+
194
+ ## 💬 Слэш-команды в поле ввода
195
+
196
+ В поле ввода сообщения доступны быстрые команды (начинаются с `/`):
197
+
198
+ - `/help` — открыть окно помощи;
199
+ - `/info` — подробная информация о текущем чате;
200
+ - `/sendfile` — открыть окно отправки файла;
201
+ - `/sendfile <путь>` — отправить файл сразу;
202
+ - `/sendfile <путь> -- <подпись>` — файл с подписью;
203
+ - `/sendfile <путь> | <путь> -- <подпись>` — альбом из нескольких файлов;
204
+ - `/clear` — очистить экранную ленту сообщений;
205
+ - `/logout` — выйти из аккаунта Telegram.
206
+
207
+ ---
208
+
209
+ ## 📎 Отправка файлов и картинок
210
+
211
+ Три способа: окно `Ctrl+O`, слэш-команда `/sendfile`, консольная команда `sendfile`.
212
+
213
+ **Окно отправки (`Ctrl+O`)**
214
+
215
+ - `Ctrl+F` — обзор файлов: навигация по папкам стрелками, `Enter` — войти в папку или выбрать файл, `Esc` — назад. Внизу показывается размер подсвеченного файла.
216
+ - Путь можно и просто ввести: понимает `~/Desktop/foto.png`, пути в кавычках и с экранированными пробелами — то есть перетаскивание файла прямо в терминал работает.
217
+ - Несколько файлов — через `|` в поле пути (или добавляйте их по одному через обзор). До 10 штук уходят одним альбомом.
218
+ - `Ctrl+D` — отправить без сжатия, документом. Полезно, когда важно исходное качество картинки.
219
+ - Строка под чекбоксом сразу показывает, что именно уйдёт: `✓ photo.png · 2.4 MB · уйдёт как фото`.
220
+ - Если в этот момент активен режим ответа (`Ctrl+R`), файл уйдёт **ответом** на сообщение.
221
+
222
+ **Что уходит фото, а что документом**
223
+
224
+ `.png`, `.jpg`, `.jpeg` Telegram принимает как сжатое фото; видеоформаты (`.mp4`, `.mov`, `.mkv` и т.п.) — как видео; всё остальное, включая `.webp` и `.heic`, — документом. Флажок «Как файл, без сжатия» (`Ctrl+D`, в CLI — `--as-file`) заставляет отправить документом что угодно.
225
+
226
+ **Прогресс и отмена**
227
+
228
+ Во время загрузки в строке состояния идут проценты. `Esc` прерывает отправку.
229
+
230
+ **Скачивание входящих**
231
+
232
+ `Ctrl+A` на сообщении → «Скачать медиа-вложение». Файл сохранится в `data/downloads/`.
233
+
234
+ ---
235
+
236
+ ## 🎨 Темы оформления
237
+
238
+ Тема задаётся в `.env`:
239
+
240
+ ```env
241
+ TUI_THEME=default # тёмная (по умолчанию)
242
+ TUI_THEME=nord # Nord
243
+ TUI_THEME=light # светлая
244
+ ```
245
+
246
+ Все цвета заданы hex-значениями и сводятся к палитре xterm-256 (индексы ≥ 16).
247
+ Именованные цвета (`blue`, `cyan`, `gray`) специально не используются: они
248
+ занимают индексы 0–15, которые тема терминала перекрашивает по-своему — из-за
249
+ этого синий фон мог рисоваться бирюзовым, а серый текст пропадать совсем.
250
+
251
+ Контраст каждой пары «текст на фоне» проверяется автотестом по WCAG (порог 3:1)
252
+ уже **после** конверсии в xterm-256, то есть ровно в том виде, в каком цвет
253
+ увидит пользователь.
254
+
255
+ ---
256
+
257
+ ## 🛠️ Использование через консоль (CLI)
258
+
259
+ TuiGram можно запускать в режиме консольных утилит (при запуске из клона
260
+ репозитория подставьте `node bin/tuigram.js` вместо `tuigram`):
261
+
262
+ ```bash
263
+ # Авторизация
264
+ tuigram login
265
+
266
+ # Список диалогов
267
+ tuigram dialogs --limit 30
268
+
269
+ # Просмотр истории чата (@username, ID или me для «Избранного»)
270
+ tuigram history @durov --limit 20
271
+ tuigram history me
272
+
273
+ # Отправка текстового сообщения
274
+ tuigram send me "Привет из терминала!"
275
+ tuigram send @friend "Встречаемся в 18:00"
276
+
277
+ # Отправка файла (несколько путей уходят одним альбомом)
278
+ tuigram sendfile me ./screenshot.png
279
+ tuigram sendfile me ~/a.png ~/b.png --caption "Две картинки"
280
+ tuigram sendfile me ~/photo.png --as-file # без сжатия, документом
281
+
282
+ # Живой стрим обновлений в реальном времени
283
+ tuigram listen
284
+ ```
285
+
286
+ ---
287
+
288
+ ## 📁 Структура проекта
289
+
290
+ ```
291
+ TuiGram/
292
+ ├── bin/
293
+ │ └── tuigram.js # CLI исполняемый файл
294
+ ├── src/
295
+ │ ├── index.js # Главная точка входа (TUI / CLI роутер)
296
+ │ ├── config.js # Пути пользовательских директорий и загрузчик .env
297
+ │ ├── state.js # Реактивное централизованное хранилище состояния
298
+ │ ├── telegram/
299
+ │ │ ├── client.js # Создание и управление MTProto клиентом
300
+ │ │ ├── auth.js # Интерактивный логин-визард и 2FA
301
+ │ │ ├── dialogs.js # Получение, фильтрация и поиск диалогов
302
+ │ │ ├── messages.js # Загрузка истории, отправка, правка, файлы, реакции
303
+ │ │ ├── listener.js # Живой фоновый слушатель событий MTProto
304
+ │ │ ├── entities.js # Парсинг пиров, типы чатов и кэш сущностей
305
+ │ │ └── formatter.js # Telegram Entities -> Blessed ANSI форматирование
306
+ │ ├── ui/
307
+ │ │ ├── screen.js # Управление Blessed Screen
308
+ │ │ ├── theme.js # Темы оформления (Default Dark, Nord, Light)
309
+ │ │ ├── app.js # Главный координатор интерфейса
310
+ │ │ └── components/
311
+ │ │ ├── header.js # Верхняя шапка и статус подключения
312
+ │ │ ├── chatList.js # Список диалогов со скроллом, табами и поиском
313
+ │ │ ├── chatView.js # Лента сообщений с автоскроллом и форматированием
314
+ │ │ ├── inputBox.js # Поле ввода с плашкой Reply/Edit и историей
315
+ │ │ ├── statusBar.js # Нижняя строка подсказок и тостов
316
+ │ │ └── modals/
317
+ │ │ ├── helpModal.js # Окно справки
318
+ │ │ ├── chatInfoModal.js # Окно информации о чате
319
+ │ │ ├── actionModal.js # Меню действий с сообщением
320
+ │ │ ├── fileModal.js # Диалог отправки файла
321
+ │ │ └── confirmModal.js # Диалог подтверждения
322
+ │ ├── cli/
323
+ │ │ ├── cliCommands.js # Автономные CLI команды
324
+ │ │ ├── init.js # tuigram init / paths — настройка и диагностика
325
+ │ │ └── formatters.js # Консольные форматтеры таблиц и логов
326
+ │ └── utils/
327
+ │ ├── storage.js # Файловые операции и сохранение сессий
328
+ │ └── time.js # Форматирование времени и дат
329
+ ├── scripts/
330
+ │ └── check-package.js # Предпубликационная проверка npm-пакета
331
+ ├── test/
332
+ │ └── unit.test.js # Юнит-тесты
333
+ ├── .env.example # Пример конфигурации
334
+ ├── run.sh # Меню-диспетчер для разработки
335
+ ├── LICENSE
336
+ ├── package.json
337
+ └── README.md
338
+ ```
339
+
340
+ > В опубликованный npm-пакет попадают только `bin/`, `src/`, `README.md`, `LICENSE`
341
+ > и `.env.example` — см. поле `files` в `package.json`.
342
+
343
+ ---
344
+
345
+ ## 🔒 Безопасность
346
+
347
+ **Где лежит авторизация.** Единственный файл — `session.txt` в директории данных
348
+ (`~/.local/share/tuigram/` или `%LOCALAPPDATA%\tuigram\`; путь можно сменить через
349
+ `TUIGRAM_DATA_DIR`, посмотреть — командой `tuigram paths`). Внутри строка
350
+ `StringSession` от MTProto: версия формата, номер дата-центра, его адрес и порт,
351
+ и 256-байтный `authKey`, закодированные в base64. Пароля и кода подтверждения там нет.
352
+
353
+ - Файл сессии и файл настроек сохраняются с правами `0600` — чтение и запись только владельцу.
354
+ - Ни сессия, ни ключи не лежат внутри пакета: `npm update` их не затрагивает.
355
+ - `data/` и `.env` в `.gitignore` и исключены из npm-пакета полем `files`;
356
+ проверка `node scripts/check-package.js` падает, если секрет всё же попал в сборку.
357
+ - **`authKey` хранится в открытом виде**: base64 — это кодирование, а не шифрование.
358
+ Кто прочитает файл, тот получит полный доступ к аккаунту без телефона, кода и 2FA.
359
+ Не кладите его в общие папки и незашифрованные бэкапы. Если файл утёк —
360
+ завершите сеанс в официальном клиенте («Настройки → Устройства»), это отзовёт ключ
361
+ на сервере, и строка станет бесполезной.
362
+ - `/logout` в TUI отзывает ключ на сервере и удаляет файл сессии.
363
+ - Никакие данные не передаются сторонним серверам — прямое соединение с официальными
364
+ серверами Telegram MTProto.
package/bin/tuigram.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../src/index.js";
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@emaxe/tuigram",
3
+ "version": "1.0.0",
4
+ "description": "Полнофункциональный TUI & CLI клиент Telegram на Node.js на базе MTProto (teleproto)",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.js"
8
+ },
9
+ "bin": {
10
+ "tuigram": "bin/tuigram.js"
11
+ },
12
+ "files": [
13
+ "bin/",
14
+ "src/",
15
+ "README.md",
16
+ "LICENSE",
17
+ ".env.example"
18
+ ],
19
+ "keywords": [
20
+ "telegram",
21
+ "tui",
22
+ "cli",
23
+ "terminal",
24
+ "mtproto",
25
+ "teleproto",
26
+ "chat",
27
+ "messenger",
28
+ "blessed",
29
+ "terminal-client"
30
+ ],
31
+ "author": "Maksim Klisin <emaxe.work@gmail.com>",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/emaxe/tuigram.git"
35
+ },
36
+ "homepage": "https://github.com/emaxe/tuigram#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/emaxe/tuigram/issues"
39
+ },
40
+ "license": "MIT",
41
+ "engines": {
42
+ "node": ">=18"
43
+ },
44
+ "os": [
45
+ "darwin",
46
+ "linux",
47
+ "win32"
48
+ ],
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "scripts": {
53
+ "start": "node src/index.js",
54
+ "menu": "./run.sh",
55
+ "tui": "node src/index.js tui",
56
+ "init": "node src/index.js init",
57
+ "login": "node src/index.js login",
58
+ "dialogs": "node src/index.js dialogs",
59
+ "history": "node src/index.js history",
60
+ "send": "node src/index.js send",
61
+ "listen": "node src/index.js listen",
62
+ "test": "node test/unit.test.js",
63
+ "prepublishOnly": "node test/unit.test.js && node scripts/check-package.js"
64
+ },
65
+ "dependencies": {
66
+ "big-integer": "^1.6.52",
67
+ "colorette": "^2.0.20",
68
+ "dotenv": "^16.4.7",
69
+ "input": "^1.0.1",
70
+ "neo-blessed": "^0.2.0",
71
+ "teleproto": "^1.229.0"
72
+ }
73
+ }
@@ -0,0 +1,164 @@
1
+ import { connectClient, withClient } from "../telegram/client.js";
2
+ import { loginInteractive } from "../telegram/auth.js";
3
+ import { fetchDialogs } from "../telegram/dialogs.js";
4
+ import { fetchHistory, sendMessage, sendFiles } from "../telegram/messages.js";
5
+ import { inspectLocalFile } from "../utils/storage.js";
6
+ import { formatFileSize } from "../utils/time.js";
7
+ import { startTelegramListener } from "../telegram/listener.js";
8
+ import { formatDialogRow, formatHistoryMessage, formatStreamEvent } from "./formatters.js";
9
+ import { green, bold, red, yellow } from "colorette";
10
+
11
+ /**
12
+ * Авторизация в аккаунте через CLI.
13
+ */
14
+ export async function cmdLogin() {
15
+ console.log(bold("\n🔑 Интерактивная авторизация в Telegram:\n"));
16
+ await loginInteractive();
17
+ }
18
+
19
+ /**
20
+ * Вывод списка диалогов в консоль.
21
+ * @param {object} flags
22
+ */
23
+ export async function cmdDialogs(flags) {
24
+ await withClient(async (client) => {
25
+ const limit = flags.limit ? parseInt(flags.limit, 10) : 50;
26
+ const archived = flags.archived === true ? true : undefined;
27
+
28
+ console.log(bold(`\n📂 Загрузка диалогов (макс. ${limit})...\n`));
29
+ const dialogs = await fetchDialogs(client, { limit, archived });
30
+
31
+ for (const d of dialogs) {
32
+ console.log(formatDialogRow(d));
33
+ }
34
+
35
+ console.log(gray(`\nВсего получено: ${dialogs.length} диалогов\n`));
36
+ });
37
+ }
38
+
39
+ /**
40
+ * Вывод истории сообщений чата.
41
+ * @param {string} peer
42
+ * @param {object} flags
43
+ */
44
+ export async function cmdHistory(peer, flags) {
45
+ if (!peer) {
46
+ throw new Error("Укажите идентификатор чата: tuigram history <@username | -100... | me>");
47
+ }
48
+
49
+ await withClient(async (client) => {
50
+ const limit = flags.limit ? parseInt(flags.limit, 10) : 30;
51
+ console.log(bold(`\n💬 Загрузка истории для ${peer} (макс. ${limit} сообщений)...\n`));
52
+
53
+ const result = await fetchHistory(client, peer, { limit });
54
+ const messages = [...result.messages].reverse();
55
+
56
+ for (const m of messages) {
57
+ console.log(formatHistoryMessage(m));
58
+ }
59
+
60
+ console.log(gray(`\nВсего отображено: ${messages.length} сообщений\n`));
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Отправка текстового сообщения из CLI.
66
+ * @param {string} peer
67
+ * @param {string} text
68
+ * @param {object} flags
69
+ */
70
+ export async function cmdSend(peer, text, flags) {
71
+ if (!peer || !text) {
72
+ throw new Error("Использование: tuigram send <@username|me|id> <текст сообщения>");
73
+ }
74
+
75
+ await withClient(async (client) => {
76
+ const replyTo = flags.reply ? parseInt(flags.reply, 10) : undefined;
77
+ console.log(yellow(`Отправка сообщения в ${peer}...`));
78
+ const sent = await sendMessage(client, peer, text, { replyTo });
79
+ console.log(green(`✓ Сообщение успешно отправлено! (ID: ${sent.id})`));
80
+ });
81
+ }
82
+
83
+ /**
84
+ * Отправка файла из CLI.
85
+ * @param {string} peer
86
+ * @param {string} filePath
87
+ * @param {object} flags
88
+ */
89
+ export async function cmdSendFile(peer, filePaths, flags) {
90
+ const rawPaths = (Array.isArray(filePaths) ? filePaths : [filePaths]).filter(Boolean);
91
+ if (!peer || rawPaths.length === 0) {
92
+ throw new Error(
93
+ "Использование: tuigram sendfile <@username|me|id> <путь> [ещё_путь ...] [--caption \"текст\"] [--as-file]"
94
+ );
95
+ }
96
+
97
+ // Валидируем до подключения: понятная ошибка вместо сетевой
98
+ const files = [];
99
+ for (const raw of rawPaths) {
100
+ const info = inspectLocalFile(raw);
101
+ if (!info.ok) throw new Error(info.error);
102
+ files.push(info);
103
+ }
104
+
105
+ const totalSize = files.reduce((sum, f) => sum + f.size, 0);
106
+ const label = files.length === 1 ? files[0].name : `${files.length} файлов`;
107
+
108
+ await withClient(async (client) => {
109
+ const caption = flags.caption || "";
110
+ const forceDocument = Boolean(flags["as-file"] || flags.document);
111
+
112
+ console.log(yellow(`Отправка ${label} (${formatFileSize(totalSize)}) в ${peer}...`));
113
+
114
+ let lastPercent = -1;
115
+ const progressCallback = (progress) => {
116
+ const percent = Math.min(99, Math.round((progress || 0) * 100));
117
+ if (percent === lastPercent) return;
118
+ lastPercent = percent;
119
+ process.stdout.write(`\rЗагрузка: ${percent}% `);
120
+ };
121
+
122
+ const sent = await sendFiles(client, peer, files.map((f) => f.filePath), {
123
+ caption,
124
+ forceDocument,
125
+ progressCallback,
126
+ });
127
+
128
+ process.stdout.write("\r \r");
129
+ const ids = sent.map((m) => m.id).join(", ");
130
+ console.log(green(`✓ Успешно отправлено! (ID: ${ids})`));
131
+ });
132
+ }
133
+
134
+ /**
135
+ * Режим непрерывного прослушивания живых событий в консоли.
136
+ */
137
+ export async function cmdListen() {
138
+ const client = await connectClient();
139
+ const me = await client.getMe();
140
+
141
+ console.log(green(bold(`\n🟢 Подключено как: ${me.firstName || ""} ${me.lastName || ""} (@${me.username || me.id})`)));
142
+ console.log(yellow("Слушаю обновления в реальном времени... Нажмите Ctrl+C для выхода.\n"));
143
+
144
+ const listener = startTelegramListener(client);
145
+
146
+ listener.on("new_message", (data) => console.log(formatStreamEvent("new_message", data)));
147
+ listener.on("edited_message", (data) => console.log(formatStreamEvent("edited_message", data)));
148
+ listener.on("deleted_messages", (data) => console.log(formatStreamEvent("deleted_messages", data)));
149
+ listener.on("typing", (data) => console.log(formatStreamEvent("typing", data)));
150
+
151
+ const shutdown = async () => {
152
+ console.log(yellow("\nОстановка слушателя..."));
153
+ listener.stop();
154
+ await client.disconnect().catch(() => {});
155
+ process.exit(0);
156
+ };
157
+
158
+ process.on("SIGINT", shutdown);
159
+ process.on("SIGTERM", shutdown);
160
+ }
161
+
162
+ function gray(text) {
163
+ return `\x1b[90m${text}\x1b[0m`;
164
+ }