Вводные: от рутины к инструменту
Я работаю аналитиком по потерям на складе одного крупного маркетплейса. Типовая задача: быстро понять, какой товар “завис”, какой скоро уйдёт на списание, и куда смотреть в первую очередь, чтобы снижать потери.
До автоматизации процесс выглядел так:
Берём Excel-выгрузки из внутренних систем.
Собираем сводные таблицы в Excel.
Приводим формат, фильтруем, считаем суммы/количество.
Выгружаем результат в Google Sheets (чтобы коллегам было удобно смотреть).
Руками проверяем повторы и корректность группировок.
На один цикл уходило около двух часов времени, а ещё периодически всплывали “мелкие” ошибки: повторяющиеся позиции, неверные фильтры, случайные сдвиги диапазонов, забытый формат даты.
Я поймал себя на мысли: мы не решаем задачу аналитики — мы обслуживаем процесс выгрузки.
Так родилась идея: сделать бота, который берёт выгрузки, сам считает нужные агрегации и кладёт результат в Google Sheets в готовом виде.

Я сформулировал “MVP-требования”, чтобы не закопаться:
В Telegram выбираю режим (какой файл обрабатываем).
Бот принимает Excel, обрабатывает и выгружает в Google Sheets.
Всё логируется: кто отправил, какой файл, что получилось.
Никаких ручных сводных таблиц.
Дополнительная ценность: отдельный блок “что спишется в течение 24 часов”, чтобы контролировать срочные потери.
Проект оказался не только автоматизацией одной рутины, но и новым инструментом: он помогает сразу видеть приоритет по списаниям и быстрее принимать решения.
Архитектура: почему Telegram и Google Sheets
Почему Telegram:
Он уже у всех есть.
Не нужно UI/веб-приложение.
Можно встроить “процесс” в привычный мессенджер.
Почему Google Sheets:
Удобно для совместного доступа.
Не нужно поднимать BI, если задача — оперативный контроль.
Таблицу можно расширять (дашборды, фильтры, условное форматирование).

Первая версия: один файл, один сценарий
Начинал я с одного режима: “выгрузка товара без движения”.
Выгрузка содержит записи по товарам. Нам было важно получить таблицу примерно в таком виде:
Гофра / ID тары
Идентификатор товара
Количество
Стоимость
Причём “Идентификатор товара” внутри одной гофры хотелось видеть списком (через переносы строк), чтобы можно было быстро копировать и искать повторяющиеся позиции.
Бизнес-логика агрегирования (упрощённо)
Группируем по ID тары.
Для каждой тары:
список идентификаторов товара (join через
\n)количество строк (count)
сумма стоимости (sum)
Быстро выяснилось: всё упирается в доставку файла
Telegram хорош, пока файлы маленькие. Но в реальности Excel-выгрузки иногда бывают > 20 МБ и Telegram отказывает: BadRequest: File is too big.
Сначала я хотел сделать скачивание по ссылке с Яндекс.Диска — и тут вылезла проблема SmartCaptcha/ограничений для публичных ссылок.
Решение оказалось инженерно простым и надёжным: OAuth и доступ к Диску через API.
Итерация 2: Яндекс.Диск OAuth и “берём последний файл из папки”
Я сделал сценарий “бот как инструмент”, а не “бот как файлообменник”:
На Яндекс.Диске есть две папки:
/BOT_UPLOADS/no_move//BOT_UPLOADS/24h/
Сотрудники загружают файлы туда (через общий доступ по приглашению).
Бот по OAuth берёт последний загруженный файл из нужной папки и обрабатывает.
Плюсы:
Telegram больше не лимитирует размер файлов.
Не нужно пересылать файлы в чат (а с РФ это иногда и правда проблемно).
Процесс становится предсказуемым: “закинул на диск → нажал кнопку → получил результат”.

Интерфейс бота: минимум кнопок — максимум ясности
В Telegram я сделал постоянную клавиатуру:
📦 Без движения
⏱ 24 часа (обновить)
☁️ Взять с Я.Диска (последний файл)
🛠 Админ-панель (только для admin user_id)
Логика работы простая:
выбираешь режим (какой файл мы ждём)
либо отправляешь Excel (если маленький), либо жмёшь “взять с диска”
Важный UX момент: если пользователь отправляет файл “не выбрав режим”, бот не падает и не пытается угадывать — он просит выбрать режим.

Две выгрузки на одном листе: как я спроектировал Google Sheets
Изначально хотелось, чтобы всё было “на одном экране”:
слева: “Без движения”
справа: “Списание в течение 24 часов”
сверху: аккуратные заголовки
справа ещё блок “Актуальность файла 24ч” (дата/время последнего обновления)
Схематично:
Левая зона:
B2:E…Правая зона:
K2:O…Метаданные актуальности:
P3:P4
Так таблица превращается в инструмент: зашёл — сразу видишь “что висит” и “что срочно спишется”.

Итерация 3: второй файл — “спишется в течение 24 часов”
С файлом “24 часа” были нюансы:
Там встречаются пустые ячейки.
В нём данные по всем складам, а мне нужно только определённые блоки (whitelist по “ID Блока”).
В нём почти всегда пустая “гофра/ID тары”.
Третий пункт особенно важный: если в файле 24ч ID тары пустой, то группировать по нему бессмысленно — получается одна гигантская группа “—”.
Значит ID тары нужно брать из выгрузки “без движения”.
Главная идея правого блока: “соединить два мира”
Получилась логика join’а:
“Без движения” даёт нам:
ID тары
список идентификаторов товара
“24 часа” даёт нам:
прогноз списания (дата/время)
стоимость
идентификатор товара (в этом файле он называется “Идентификатор товара”)
Что делаем:
строим карту
идентификатор товара -> ID тарыиз левой таблицы
(там идентификаторы хранятся списком через\n, поэтому мы их разбиваем)берём snapshot “24 часа” (после whitelist фильтра по ID Блока)
оставляем только те строки “24 часа”, чей идентификатор есть в карте
присваиваем каждой строке “24 часа” ID тары из карты
группируем по ID тары и считаем:
список идентификаторов товара
количество
сумма стоимости
когда начнёт списываться? — это
MIN(прогноз)в группе
(самый ранний товар, который уйдёт на списание)
И вот тут появилось то, чего в Excel сводными было достигать долго:
приоритизация по ближайшему списанию стала естественной.

Snapshot’ы: чтобы не зависеть от “сегодня загрузил / не загрузил”
Чтобы правый блок можно было строить даже если “без движения” обновляли не прямо сейчас, я добавил хранение:
last_24h_snapshot— обработанные данные по 24 часам (после whitelist)last_no_move_map— картаидентификатор -> ID тары
В итоге процесс стал устойчивее: обновил 24ч — он запомнился. Обновил “без движения” — карта обновилась. А правый блок строится на пересечении.
Логи и админка: почему это важно
Любой бот, который используют несколько людей, должен объяснять, “что произошло”:
кто запросил выгрузку
какой режим
откуда взят файл (Telegram / Я.Диск)
имя файла, размер, время обработки
сколько строк/групп получилось
какие ошибки
Это сильно ускоряет поддержку: вместо “что-то не работает” есть конкретная точка.
Также важно: не логировать секреты (токены, ключи), максимум — префиксы, чтобы понять “какой токен подхватился”.
2026-03-03 01:39:56 INFO httpx: HTTP Request: POST https://api.telegram.org/bot[REDACTED]/getUpdates "HTTP/1.1 200 OK" 2026-03-03 01:39:56 INFO bot.handlers: Получен файл от user_id=*** username=*** filename=ШК без движения (2).xlsx size=1658962 2026-03-03 01:39:56 INFO httpx: HTTP Request: POST https://api.telegram.org/bot[REDACTED]/getFile "HTTP/1.1 200 OK" 2026-03-03 01:39:56 INFO httpx: HTTP Request: GET https://api.telegram.org/file/***/documents/file_68.xlsx "HTTP/1.1 200 OK" 2026-03-03 01:39:57 INFO httpx: HTTP Request: POST https://api.telegram.org/bot[REDACTED]/sendMessage "HTTP/1.1 200 OK" 2026-03-03 01:39:59 INFO httpx: HTTP Request: POST https://api.telegram.org/bot[REDACTED]/getUpdates "HTTP/1.1 200 OK" 2026-03-03 01:39:59 INFO httpx: HTTP Request: POST https://api.telegram.org/bot[REDACTED]/answerCallbackQuery "HTTP/1.1 200 OK" 2026-03-03 01:39:59 INFO httpx: HTTP Request: POST https://api.telegram.org/bot[REDACTED]/editMessageReplyMarkup "HTTP/1.1 200 OK" 2026-03-03 01:39:59 INFO httpx: HTTP Request: POST https://api.telegram.org/bot[REDACTED]/sendMessage "HTTP/1.1 200 OK" 2026-03-03 01:40:00 INFO httpx: HTTP Request: POST https://api.telegram.org/bot[REDACTED]/editMessageText "HTTP/1.1 200 OK" 2026-03-03 01:40:07 INFO root: Данные успешно загружены в Google Sheets. Строк: 56. Диапазон: D1:G56. Лист: Выгрузка *** 2026-03-03 01:40:07 INFO httpx: HTTP Request: POST https://api.telegram.org/bot[REDACTED]/editMessageText "HTTP/1.1 200 OK" 2026-03-03 01:40:07 INFO bot.handlers: Успешная загрузка: user_id=*** username=*** mode=export_with_transfers rows=56 source_rows=2320 duration=7.109s processing=0.575s upload=6.397s file=ШК без движения (2).xlsx size=1658962 unknown=1 2026-03-03 01:40:07 INFO bot.handlers: Удален временный файл: C:\Users\user\PyCharmMiscProject\Bot_Mont_SHK\uploaded_710953844_1772491196.xlsx 2026-03-03 01:40:09 INFO httpx: HTTP Request: POST https://api.telegram.org/bot[REDACTED]/getUpdates "HTTP/1.1 200 OK"
CI: чтобы не ломать всё одним коммитом
Проект живёт и меняется. Чтобы случайно не сломать зависимость/синтаксис, я сделал простой CI в GitHub Actions:
установка
requirements.txtpython -m compileall .ruff checkпо критичным ошибкам
Это минимально, но уже ловит “упал импорт”, “ошибка синтаксиса”, “битая зависимость”.
Результаты: что изменилось в работе
Самое заметное:
Ушёл час рутины на сводные и ручные проверки.
Процесс выгрузки стал “одна кнопка”.
Появился новый инструмент контроля:
“что зависло”
“что спишется в ближайшие 24 часа”
какой товар “горит” (min по времени списания)
Это не просто автоматизация: это улучшение управления потерями

С какими проблемами столкнулся по дороге
1) Лимиты Telegram на размер файлов
Решение: Яндекс.Диск + OAuth + “берём последний файл”.
2) SmartCaptcha на публичных ссылках Яндекс.Диска
Решение: не использовать публичные ссылки, только OAuth API.
3) Плавающие форматы Excel и пустые ячейки
Решение: аккуратное чтение, приведение типов, пропуски, логирование отброшенных строк.
4) “Токены внезапно перестали работать”
Решение: строгая проверка токена через GET /v1/disk, хранение токенов без кавычек, в идеале — авто-refresh по refresh_token.
Что можно улучшить дальше
Несколько идей “на будущее”:
Дашборд в Google Sheets:
сколько и на какую сумму спишется по часам
график “нагрузка списаний” на ближайшие 24 часа
Авто-refresh OAuth токена по refresh_token (чтобы не ловить 401).
Архив выгрузок по датам (история изменений).
Роли/клиенты (если делать “бот как сервис” для нескольких складов).
Вывод
Я начал с простой боли — «слишком много времени на сводные и ручную выгрузку».
А закончил инструментом, который:
автоматизирует сбор и обработку данных,
снижает ошибки,
делает контроль списаний прозрачнее,
и реально помогает эффективнее работать с потерями.
