Подписка на блог

Тимур Полатов

делю на ноль, обогащаю уран

Telegram-бот для семейного бюджета: как перестать тревожиться и накопить на Ford Focus

Мы тут своей микросемьей ведём микросемейный микробюджет в Google Таблице. Таблица вроде как хорошая, но заполнять её с телефона неудобно: открыть, найти строку, ввести сумму, выбрать категорию. Морока знатная

Навайбкодил связку Telegram-бот+Google Таблица (на самом деле сложнее, но об этом ниже), где просто пишем боту в Telegram, как человеку: «обед 3200, такси 2450». Дальше бот сам разбирает сообщение, ставит категорию и дописывает строки в таблицу.

Ниже описываю как он устроен, на какие грабли можно наступить и как повторить такое у себя.

Все суммы и чеки в этом посте выдуманы, валюта в самой крепкой валюте мира – тенге. Имена и потребность в ведении бюджета настоящие.

Что умеет бот (не техническая часть)

Если коротко: записывает расходы и доходы, понимает даты, шлёт напоминания, строит отчёты и каждый месяц сам заводит новый файл.

Приветствие бота по команде /start

Запись обычным текстом. Одно сообщение может содержать несколько трат. Бот отвечает подтверждением и кнопкой «Удалить».

Две траты в одном сообщении

Даты задним числом. «Вчера продукты 15620» или «12.09 такси 2980». Модель получает сегодняшнюю дату и сама считает дату траты. Назад можно до 45 дней.

Запись с указанием даты

Фото чека. Присылаю снимок, получаю список позиций с категориями.

Чек на 13 910 тенге, разобранный на 6 позиций

Уведомления друг другу. Когда я что-то записываю, собюджетница видит копию, и наоборот. Это же касается удалений.

Уведомление партнёру о новой записи

Удаление ошибок. Собюджетница написала «аптека 38600» вместо 3860. Удалить можно командой «удали последнее» или кнопкой в два нажатия: «Удалить», потом «Да, удалить». Так случайное нажатие ничего не стирает.

Удаление записи кнопкой

Напоминания. Три раза в день: в 10:00, 15:00 и 20:30. Но только тому, кто ничего не записал с прошлого напоминания. Если записал, бот молчит. В тексте спрашивает и про траты, и про поступления денег, поэтому на напоминание можно ответить «зарплата пришла 310000».

Напоминание в 15:00 и ответ доходом

Отчёты

Под полем ввода всегда есть кнопка «📊 Отчет». Она открывает меню из пяти отчётов.

Меню отчётов

Самый любимый отчёт «План и факт». Он выглядит точно как лист «Сводка» в таблице: статья, план, факт, разница, перерасход красным. В демо-месяце мы уложились в план 700 000 ₸, потратив 671 100 ₸, но две статьи вышли за рамки: кафе и рестораны (51 000 при плане 45 000) и транспорт (44 470 при плане 40 000).

Отчёт План и факт в Telegram

Остальные четыре отчёта: расходы по категориям, по дням, доходы и расходы, а также личные расходы и транспорт по людям.

Расходы по категориям
Личные расходы и транспорт: Тимур и Кристина

Так выглядит лист «Сводка» в самой таблице.

Лист Сводка

Как это устроено (задротско-техническая часть)

Схема: Telegram, Cloudflare Worker, Apps Script, Claude Haiku, Google Таблицы и Slides
  • Google Apps Script весь бэкенд, один файл.
  • Google Таблица хранилище и формулы бюджета. Берём бесплатный шаблон «Месячный бюджет» из галереи Google.
  • Claude Haiku 4.5 через API превращает текст или фото чека в JSON: сумма, описание, категория, расход или доход.
  • Google Charts внутри Apps Script рисует диаграммы, Google Slides API рисует картинку «План и факт».
  • Cloudflare Worker на бесплатном тарифе принимает webhook от Telegram.

Отчёты строит обычный код, на них Claude не тратится вообще. Модель нужна только для разбора сообщений и чеков.

Грабли

1. Telegram не принимает webhook на Apps Script

Сначала бот опрашивал Telegram раз в минуту, и кнопки отвечали с задержкой до минуты. Казалось, что бот завис, приходилось тыкать в кнопки несколько раз. Webhook напрямую не заработал: Apps Script отвечает на POST редиректом 302, а Telegram редиректы не проходит. Решение: поставить посередине Cloudflare Worker. Он сразу отвечает Telegram «ok», а сообщение пересылает в Apps Script в фоне. Секрет webhook проверяется на стороне скрипта, а повторные доставки отсекаются по id обновления.

2. Триггер «раз в минуту» не ровно раз в минуту

Время срабатывания плавает, запуск может пропуститься. Поэтому напоминание не ждёт «ровно 15:00», а имеет окно догонки в 30 минут. Факт отправки запоминается до самой отправки, чтобы при сбое бот не слал одно и то же каждую минуту. Новый файл месяца создаётся «при первом запуске в новом месяце», а не «ровно в полночь».

3. Чьи «Личные расходы»?

В таблице у записи нет автора. По строке «Личные расходы, 6 000» не понять, кто потратил. Можно завести отдельные статьи на каждого, но список раздувается. Добавил скрытый лист «_Лог бота»: кто, когда, сколько, какая строка, какой id сообщения. Отчёт «Тимур и Кристина» считает по логу и честно предупреждает, что учитывает только записи через бота. Тот же лог позволил сделать безопасное удаление: бот стирает только свои записи и не трогает строки, которые уже правили руками. В будущем думаю это исправить как дойдут руки.

4. «Вода 300» оказалась коммуналкой

Модель увидела слово «вода» и выбрала «Коммунальные услуги». Одного списка категорий мало, нужны ещё правила на спорные случаи. В промпт добавили: коммуналка только для счетов и квитанций (свет, газ, вода по счётчику, ОСИ/КСК), а бутылка воды и напитки это «Питание».

Вода 300 попадает в Питание

5. «Вчера» первого числа

Запись «вчера такси», сделанная 1 октября, должна попасть в сентябрьский файл. Поэтому каждая запись маршрутизируется в файл по дате самой траты, а не по сегодняшней. Файл нового месяца бот создаёт сам: копирует прошлый (план по статьям переезжает), чистит транзакции и сообщает об этом в чат.

Бот создал файл октября

6. Google Slides игнорирует размер страницы

Картинку «План и факт» я хотел в портретной ориентации. Но Slides.Presentations.create размер страницы молча игнорирует, слайд всегда 16:9, и таблица обрезается. Выход: заранее сделанный пустой шаблон нужного размера. Бот копирует его, рисует таблицу, экспортирует слайд в PNG и удаляет копию. Рядом ещё мелочи: у objectId минимальная длина 5 символов, сервис Slides API нужно подключить в редакторе вручную (иначе 403), а у ячеек есть внутренние отступы, из-за которых таблица «чуть-чуть подрезана снизу». Размер шрифта пришлось подбирать под число строк.

7. Мелочи редактора Apps Script

  • Кнопка «Выполнить» после перезагрузки иногда запускает первую функцию в списке, а не выбранную. Так одна тестовая функция случайно создала два лишних файла. Проверяйте, что выбрано.
  • Изменения не сохранены, пока сверху не написано «Сохранено на Диске».
  • После правки кода webhook работает на старой версии, пока не обновить развёртывание (Развернуть, Управление развёртываниями, новая версия, URL остаётся тем же).

Как повторить у себя (если очень хочется)

  1. Создайте бота у @BotFather и получите токен.
  2. Сделайте таблицу из шаблона «Месячный бюджет» (Google Таблицы, галерея шаблонов, «Личное») и положите в отдельную папку.
  3. Создайте проект Apps Script, вставьте код. Токен бота и ключ Anthropic храните только в Script Properties.
  4. Подключите сервис Google Slides API и сделайте презентацию-шаблон нужного размера.
  5. Поставьте триггер по времени раз в минуту (напоминания и новый месяц).
  6. Разверните скрипт как веб-приложение, создайте Cloudflare Worker и один раз запустите настройку webhook.

Для тех, кто дочитал до конца — промпт для Claude, который сделает все за вас

Ниже один промпт, в который уже зашиты все грабли из этого поста. Вставьте его целиком и поправьте то, что в квадратных скобках.

Промпт: собрать такого бота с нуля одним сообщением
Сделай Telegram-бота для семейного бюджета на Google Apps Script. Код одним файлом .gs, комментарии на русском.
Пользуются двое: [Имя 1] и [Имя 2], валюта [тенге], часовой пояс [Asia/Almaty].

**Хранилище.** Google Таблица из стандартного шаблона «Месячный бюджет» (листы «Транзакции» и «Сводка»).
На «Транзакциях» два блока с заголовками «Дата, Сумма, Описание, Категория»: слева расходы, справа доходы.
Строку заголовков ищи по двум ячейкам «Дата», не по номерам колонок. Все названия листов и заголовков вынеси
в константы в начале файла. Каждый месяц отдельный файл «Месячный бюджет. <Месяц> <Год>» в папке [ID папки].

**Разбор сообщений.** Текст и фото чека отправляй в Claude API (модель claude-haiku-4-5), получай JSON-массив
записей: дата, сумма, описание, категория, тип (расход или доход). В одном сообщении может быть несколько трат.
Передавай модели сегодняшнюю дату, день недели и имя автора, чтобы она понимала «вчера», «в пятницу», «12.09»
(не дальше 45 дней назад).
Категории расходов: [список]. Категории доходов: [список].
Добавь правило: «Коммунальные услуги» только для счетов и квитанций (свет, газ, вода по счётчику, мусор, ОСИ/КСК);
покупка питьевой воды и напитков это «Питание».

**Запись.** Каждую запись клади в файл месяца по дате траты, а не по сегодняшней дате.
Параллельно веди скрытый лист «_Лог бота»: когда внесено, кто, тип, дата, сумма, описание, категория,
номер строки в «Транзакциях», id сообщения, статус. Используй LockService, чтобы две записи не легли в одну строку.

**Ответы.** Подтверждение «Записал: обед, 3200 тенге, Кафе» с inline-кнопкой «❌ Удалить».
Копия каждой записи и удаления уходит второму человеку («[Имя 1] записал(а): …»).
Постоянная клавиатура с одной кнопкой «📊 Отчет». Команды /start (краткая справка) и /report.
Сообщение из незнакомого чата: ответить его chat id, больше ничего не делать.

**Удаление.** «❌ Удалить» → «Да, удалить / Отмена» → строки этой записи очищаются, в сообщение дописывается
«❌ Удалено из таблицы». Команда «удали последнее» удаляет последнюю запись автора без подтверждения.
Удалять можно только свои записи. Если строку в таблице уже меняли руками, не трогай её, а напиши, что проверить.

**Новый месяц.** При первом запуске в новом месяце скопируй файл прошлого месяца (план по статьям сохраняется),
очисти транзакции и лог, напиши [Имя 1] в Telegram, что файл создан.

**Напоминания** в 10:00, 15:00 и 20:30 только тем, кто ничего не записал с прошлого напоминания. В тексте упомяни
и траты, и доходы. Триггер Apps Script неточный: окно догонки 30 минут, факт отправки запоминай до отправки.

**Отчёты** за текущий месяц по кнопке, меню из пяти inline-кнопок:
1. По категориям (столбчатая диаграмма Charts + итог текстом).
2. По дням (сумма, среднее в день, самый затратный день).
3. [Имя 1] и [Имя 2]: кто сколько потратил на статьи [Личные расходы, Транспорт], автор берётся из лога, stacked-диаграмма.
4. Доходы и расходы, остаток.
5. План и факт: картинка, которая выглядит как таблица «Расходы» на листе «Сводка» (статья, предполагаемые,
   фактические, разница, перерасход красным), без длинного текста. Рисуй через Google Slides API:
   копируй заранее созданную портретную презентацию-шаблон [ID] (Slides.Presentations.create размер
   страницы игнорирует), objectId делай длиной от 5 символов, размер шрифта подбирай под число строк с учётом
   отступов ячеек, экспортируй слайд в PNG, временную копию удаляй.

**Скорость.** Telegram webhook, но не напрямую на Apps Script (он отвечает 302, Telegram не принимает):
дай код Cloudflare Worker, который сразу отвечает «ok», а тело пересылает в веб-приложение через
ctx.waitUntil с redirect: 'follow', передавая секрет webhook как ?tg=. В Apps Script проверяй секрет и
отбрасывай повторные update_id через CacheService. Функции setupWebhook и disableWebhook (откат на опрос раз в минуту).

**Секреты** (токен бота, ключ Anthropic, секрет webhook) только в Script Properties.

В конце дай пошаговую инструкцию по настройке: BotFather, таблица, Script Properties, сервис Slides,
триггер раз в минуту, развёртывание веб-приложения, Worker, setupWebhook, setBotCommands.

Шаблон «Месячный бюджет» доступен в галерее шаблонов Google Таблиц или  скопировать себе тут.

Если будете повторять и что-то пойдёт не так, пишите своему Claude или в комментариях, попробую ответить. Всем богатства.

LikeNotepad.exe 1.0: Блокнот для Mac

Набыдлокодил Навайбкодил LikeNotepad.exe — простой и идеальный текстовый редактора для macOS в духе классического Блокнота под этот ваш Windows. Никаких облаков, подписок и ИИ. Просто текст.

Окно LikeNotepad.exe

Зачем

На Mac не нашлось программы, которая делала бы то же, что Блокнот: открыть текстовый файл, поправить пару строк, сохранить и ничего при этом не сломать. Навайбкодил вариант, которых я подозреваю уже тысячи Карл! (на момент ресерча в июле 2026 блокнотов в духе классического было уже 7).

Что внутри

  • Меню и сочетания клавиш как в Блокноте, лишние системные пункты macOS убраны.
  • Файл сохраняется в той же кодировке (UTF-8, UTF-16, Windows-1251) и с теми же переводами строк, с какими был открыт.
  • Поиск и замена, «Перейти» к строке, перенос по словам, выбор шрифта, светлая и тёмная тема.
  • Работает пасхалка .LOG.
  • Интерфейс на восьми языках.

Полный список — на странице проекта.

Отдельно прошу оценить мою гордость — выбор шрифтов, сводит олдскулы, выбивает слезу.

Окно выбора шрифта в LikeNotepad.exe

Как установить

Скачать .dmg (Apple Silicon и Intel, macOS 10.15 и новее) или через Homebrew:

brew install --cask polatov/tap/likenotepad

Приложение не подписано сертификатом Apple, поэтому первый запуск macOS заблокирует. Открыть: «Системные настройки» → «Конфиденциальность и безопасность» → «Всё равно открыть». Один раз.

Исходники

Написано на Tauri 2 (Rust + WebKit), код открыт под MIT: github.com/polatov/likenotepad. Ошибки и пожелания — в Issues.

Функции высшего порядка в Python

Python предоставляет мощные инструменты для обработки данных, известные как функции высшего порядка. Основными представителями этой категории являются map, filter и reduce. Данные функции позволяют эффективно работать с коллекциями данных, упрощая код и повышая его читаемость.

Функции высшего порядка

Функции высшего порядка — это функции, которые могут принимать другие функции в качестве аргументов или возвращать их. В Python map, filter и reduce являются наиболее часто используемыми функциями этого типа.

1. Функция reduce

Функция reduce применяется для свертки последовательности в единое значение. Она импортируется из модуля functools.

from functools import reduce

numbers = [2, 3, 8]
max_number = reduce(lambda x, y: x if x > y else y, numbers)
print(max_number)  # Выведет: 8

В данном примере reduce используется для нахождения максимального числа в списке.

2. Функция filter

Функция filter применяется для отбора элементов последовательности, удовлетворяющих определенному условию.

numbers = [2, 3, 8, 15, 34, 42]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers)  # Выведет: [2, 8, 34, 42]

Здесь filter используется для выбора четных чисел из списка.

3. Функция map

Функция map применяет заданную функцию к каждому элементу последовательности.

numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared)  # Выведет: [1, 4, 9, 16, 25]

В этом примере map используется для возведения каждого числа в квадрат.

Рекомендации по использованию

При использовании функций высшего порядка рекомендуется избегать побочных эффектов в передаваемых функциях. Это означает, что функции не должны изменять глобальные переменные или производить вывод. Такой подход обеспечивает чистоту и предсказуемость кода.

Методы обновления множеств «по месту» в Python

В Python существуют методы для обновления множеств «по месту», что означает, что они изменяют текущее множество без создания нового. Эти методы позволяют выполнять различные операции над множествами «по месту».

1. difference_update()

Метод difference_update() удаляет из текущего множества все элементы, которые присутствуют в другом множестве. Это означает, что текущее множество становится разностью двух множеств.

Пример:

A = {1, 2, 3, 4}
B = {3, 4, 5}
A.difference_update(B)  # Теперь A содержит {1, 2}

2. intersection_update()

Метод intersection_update() оставляет в текущем множестве только элементы, которые также присутствуют в другом множестве. Это означает, что текущее множество становится пересечением двух множеств.

Пример:

A = {1, 2, 3, 4}
B = {3, 4, 5}
A.intersection_update(B)  # Теперь A содержит {3, 4}

3. symmetric_difference_update()

Метод symmetric_difference_update() заменяет текущее множество его симметричной разностью с другим множеством. Симметричная разность множества содержит элементы, которые присутствуют только в одном из множеств.

Пример:

A = {1, 2, 3, 4}
B = {3, 4, 5}
A.symmetric_difference_update(B)  # Теперь A содержит {1, 2, 5}

4. update()

Метод update() добавляет элементы из другого множества (или других итерируемых объектов) в текущее множество. Этот метод обновляет текущее множество «по месту».

Пример:

A = {1, 2, 3}
B = {3, 4, 5}
A.update(B)  # Теперь A содержит {1, 2, 3, 4, 5}

Использование методов обновления «по месту» позволяет изменять множества без необходимости создавать новые объекты, что может быть полезно при работе с большими объемами данных и требует меньше системных ресурсов.

Операции над множествами в Python: сравнение, объединение и извлечение уникальных элементов

1. Объединение (Union)

Объединение множеств — это операция, которая позволяет объединить два или более множества в одно, оставив только уникальные элементы. В Python это можно сделать с помощью оператора | или метода union().

Пример:

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}
result = A | B  # Результат: {1, 2, 3, 4, 5, 6}

2. Пересечение (Intersection)

Пересечение множеств — это операция, которая возвращает только общие элементы между двумя или более множествами. В Python это можно сделать с помощью оператора & или метода intersection().

Пример:

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}
result = A & B  # Результат: {3, 4}

3. Разность (Difference)

Разность множеств — это операция, которая возвращает элементы, присутствующие в одном множестве, но отсутствующие в другом. В Python это можно сделать с помощью оператора - или метода difference().

Пример:

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}
result1 = A - B  # Результат: {1, 2}
result2 = B - A  # Результат: {5, 6}

4. Симметрическая разность (Symmetric Difference)

Симметрическая разность множеств — это операция, которая возвращает элементы, присутствующие в одном множестве или другом, но не в обоих сразу. В Python это можно сделать с помощью оператора ^ или метода symmetric_difference().

Пример:

A = {1, 2, 3, 4}
B = {3, 4, 5, 6}
result = A ^ B  # Результат: {1, 2, 5, 6}

Шпаргалка по основам работы с Poetry

Poetry — инструмент для управления зависимостями и пакетами в проектах на Python, который решает множество проблем, связанных с установкой пакетов, управлением версиями и сборкой проекта. Преимущества использования Poetry, в том что все зависимости и их версии контролируются в одном месте, а также в том, что инструмент самостоятельно создает и управляет виртуальными средами проектов.

Для описания зависимостей и настроек проекта использует файл pyproject.toml, заменяя традиционные setup.py и requirements.txt.

Установка Poetry

curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -

Создание нового проекта

poetry new my_project

Это создаст новую структуру каталогов с начальными файлами для проекта my_project.

Управление зависимостями

poetry add package_name

Poetry автоматически обновит файл pyproject.toml и poetry.lock, обеспечивая управление версиями.

Установка зависимостей
Установка всех зависимостей, перечисленных в pyproject.toml:

poetry install

Это создаст виртуальную среду и установит в нее все необходимые зависимости.

Сборка и публикация
Poetry также упрощает сборку и публикацию пакета. Собрать пакет можно командой:

poetry build

Публикация пакета в PyPI:

poetry publish

Решение конфликта между локальной и удаленной ветками в Git

В процессе работы с Git часто возникают ситуации, когда изменения, внесенные в удаленный репозиторий, не синхронизированы с локальной копией. Это может привести к ошибкам при попытке отправить локальные изменения на удаленный сервер.

Сценарий конфликта:

1. Изменения в удаленном репозитории: Предположим, что в удаленном репозитории на GitHub были внесены изменения другими участниками проекта.

2. Локальные изменения: В то же время, разработчик вносит изменения в локальную копию проекта, не зная о новых изменениях в удаленном репозитории.

3. Попытка отправки изменений: При попытке отправить локальные изменения на GitHub, разработчик сталкивается с ошибкой, указывающей на расхождение между локальной и удаленной версиями.

Пример ошибки:

user@laptop ~/dev/my_project $ git push
To github.com:user/my_project.git
 ! [rejected]        main -> main (fetch first)
error: не удалось отправить некоторые ссылки в «github.com:user/my_project.git»
подсказка: Updates were rejected because the remote contains work that you do not
подсказка: have locally. This is usually caused by another repository pushing to
подсказка: the same ref. If you want to integrate the remote changes, use
подсказка: 'git pull' before pushing again.
подсказка: See the 'Note about fast-forwards' in 'git push --help' for details.

Методы решения:

1. Слияние (Merge): Этот метод объединяет изменения из удаленной ветки с локальными изменениями, создавая новый коммит.

git config pull.rebase false
git pull origin main

2. Перебазирование (Rebase): Перебазирование применяет локальные изменения поверх изменений удаленной ветки.

git config pull.rebase true
git pull origin main

Разрешение конфликтов:

В процессе слияния или перебазирования могут возникнуть конфликты. Потребуется вручную разрешить эти конфликты, выбрав подходящие изменения, и затем продолжить процесс слияния или перебазирования.

Как запушить изменения в GitHub

Для публикации изменений на GitHub необходимо выполнить следующие команды:

1. Добавление изменений в индекс Git:

git add .

Эта команда добавляет все измененные файлы в индекс для следующего коммита. Можно указать конкретные файлы вместо точки.

2. Создание коммита с описанием изменений:

git commit -m "Описание изменений"

Сообщение коммита должно кратко описывать произведенные изменения.

3. Отправка изменений в удаленный репозиторий:

git push origin main

origin — имя удаленного репозитория, main — имя ветки для публикации. Имя ветки может отличаться и должно соответствовать ветке, предназначенной для изменений.

При первой публикации изменений или при изменении настроек доступа Git может запросить учетные данные GitHub — имя пользователя и пароль или токен доступа.

После выполнения этих команд изменения будут отправлены в удаленный репозиторий на GitHub.

Удаление пакета в Python

Чтобы удалить пакет, установленный через pip, необходимо использовать команду pip uninstall, указав имя пакета. Имя пакета соответствует тому, что указано в сообщении об ошибке или в файле pyproject.toml. Например:

python3 -m pip uninstall package_name

Для удаления зависимости из проекта, управляемого Poetry, используется команда poetry remove, где package_name — это имя пакета, указанное в pyproject.toml. Пример:

poetry remove requests

Если требуется удалить основной пакет проекта из виртуального окружения, установленного через pip, следует использовать команду pip uninstall, как показано выше.

В случае, когда после выполнения poetry build возникает сообщение о том, что пакет уже установлен, и необходимо выполнить переустановку, следует использовать флаг —force-reinstall с командой pip install. Пример команды для переустановки пакета:

python3 -m pip install --user --force-reinstall dist/*.whl

Это приведет к переустановке пакета, даже если он уже присутствует в системе.

Некоторые комбинации клавиш в mc

Ctrl + R обновляет активную панель;
Ctrl + X C посмотреть/поменять права (chmod) для отмеченных файлов;
Ctrl + X O посмотреть/поменять владельца (chown) для отмеченных файлов;
Ctrl + S или Alt + S открывает диалог быстрого поиска (вводимые символы отображаются не в командной строке, а в строке поиска);
Ctrl + X A открывает диалог виртуальных файловых систем;
Ctrl+ \ вызывается меню часто используемых каталогов.
Ctrl + X J показываются все фоновые задачи.
Ctrl + X T послать выделенные объекты в командную строку
Ctrl + X S создать символическую ссылку (symlink)
Ctrl + X Q быстрый просмотр содержимого файла на второй панели.
Ctrl + X I быстрый просмотр информации о текущем объекте на второй панели.
ESC + T или ALT + T смена вида панели (стандартный, укороченный, расширенный);
ESC + H или ALT + H вызов списка последних выполненных команд;
Alt + G отмечает первый файл или каталог в отображаемом списке на панели
Alt + R отмечает средний файл или каталог в отображаемом списке на панели
Alt + J отмечает последний файл или каталог в отображаемом списке на панели
Alt + O неактивная панель переходит в каталог на который указывает активная панель.
Alt + . не показывать или показать скрытые файлы или каталоги.
Alt + , переключить режим отображения панелей (вертикально/горизонтально).
+ (Plus) вызывает окно для выделения группы файлов (можно использовать регулярные выражения)
\ (Backslash) снимает отметку с группы файлов (действие, обратное тому, которое
вызывается по клавише “+”).

Ранее Ctrl + ↓