В прогоне курса бот уже сохранял заявки и слал уведомления. Агент добавил поле «дата согласия» и выгрузку на Яндекс Диск, все 10 тестов прошли. Первая же живая заявка закончилась фразой «Произошла ошибка при сохранении заявки. Пожалуйста, попробуйте позже». Позже, на сервере, бот однажды стёр файл со всеми прошлыми заявками владельца, и тесты снова были зелёными. Этот урок про то, как не повторить обе ошибки у заказчика.
Чему научишься в этом уроке:
• поставить согласие первым шагом и записывать время нажатия;
• получить токен Яндекс Диска с нужными правами и выгружать заявки в zayavki.xlsx;
• менять базу без потери старых заявок и убрать из неё имя и телефон;
• проверять запись данных вживую, а не только тестами.
Шаг 1. Согласие, дата и выгрузка
Задание агенту в прогоне собрало три правки сразу. Сформулируй так же:
«Добавь кнопку “Не согласен”: вежливый отказ, ничего не сохранять, предложить /start. Сохраняй дату и время согласия. Перепиши текст согласия по фактическим полям из scenariy.md. После согласия выгружай заявку в zayavki.xlsx в папку “Кодиум” на Яндекс Диске владельца, токен Диска из .env.»
Агент справился за 6,3 минуты и 23 хода, тестов стало 10 из 10. Текст согласия перестал упоминать почту и отчество. Но новое поле consent_at (дата согласия) он добавил только в код. Об этом ниже.
Позже заказчик решил: согласие спрашиваем до сбора данных, то есть первым шагом. Это задание прогона почти дословно; в конце оригинала была ещё просьба обновить бота на сервере, у тебя сервера пока нет, эту часть мы убрали:
«Заказчик решил по 152-ФЗ: согласие спрашиваем ДО сбора данных. Переделай сценарий: /start → приветствие и согласие (кнопки «Согласен», «Не согласен», «Прочитать согласие») → только после «Согласен» спрашиваем имя → телефон → курс → заявка уходит владельцу и на Диск. «Не согласен» — вежливый отказ, ничего не спрашиваем дальше. Время согласия фиксируй в момент нажатия «Согласен». Ещё две правки: 1. Запасной файл pending.jsonl на сервере не должен содержать имя — только номер заявки, курс и время. 2. Добавь tzdata в requirements.txt (на Windows без него zoneinfo не находит Europe/Moscow). Обнови тесты, запусти их.»
Агенту хватило 5,2 минуты и 16 ходов, тестов стало 15 из 15. Живая проверка: на /start бот ответил «Здравствуйте! Добро пожаловать в онлайн-школу программирования «Кодиум»…», показал текст согласия и три кнопки, а после «Согласен» написал «Спасибо за согласие! Теперь давайте оформим заявку. Как вас зовут?». Про pending.jsonl и tzdata читай ниже, в шагах 5 и 6. Если ты собирал по сценарию из урока 2.2 и согласие у тебя уже первое, всё равно отдай эту правку: остальные пункты нужны.
Шаг 2. Токен Яндекс Диска
Чтобы бот писал в Диск владельца, нужен OAuth-токен (ключ, который разрешает программе действовать с Диском от имени человека). Выпускает его владелец Диска своим аккаунтом или ты при нём. Шаги ниже прошёл оператор курса 09.10.2026; точные надписи формы создания приложения в прогоне не снимали, поэтому сверяйся по смыслу.
- Открой oauth.yandex.ru, войди аккаунтом, на чьём Диске будут заявки. На странице «Ваши приложения» нажми «+ Создать».
- Заполни форму: название (например, «Кодиум бот»). Платформа и поля формы в прогоне не снимали; нужное поле называется «Redirect URI для веб-сервисов», впиши в него
https://oauth.yandex.ru/verification_code. - В доступах к данным отметь два права Диска: «Чтение всего Диска» и «Запись в любом месте на Диске». Сохрани.
- В карточке приложения скопируй ClientID (подпись: «Идентификатор приложения»). Client secret боту не нужен.
- Открой в браузере ссылку
https://oauth.yandex.ru/authorize?response_type=token&client_id=<ClientID>, подставив свой ClientID, и нажми «Разрешить». - На открывшейся странице будет токен. Скопируй его в .env строкой
YANDEX_DISK_TOKEN=<твой токен>(имя переменной сверь с кодом агента) и перезапусти бота.
Отмечай оба права: чтение и запись. В прогоне первый токен был только с записью. Новый файл он создавал (ответ 200), а дописать существующий не мог: Диск отвечал 500 InternalServerError, скачать файл не давал (403). Ошибка 500 звучит как «сломался Яндекс», хотя дело в правах. Добавил право в настройках приложения, получи новый токен по ссылке из пункта 5. Кабинет предупреждает, что существующие токены продолжат работу после сохранения настроек: старый токен останется со старыми правами. В прогоне так и было, и после нового токена с чтением и записью оба действия прошли с ответом 200.
Почему нужно чтение: чтобы дописать строку, бот сначала скачивает файл, добавляет заявку и загружает обратно. Без права скачать он не видит прошлые заявки.

Шаг 3. Миграция: когда меняешь базу
После шага 1 первая живая заявка упала с фразой про ошибку сохранения. В терминале было точное сообщение:
table applications has no column named consent_at
База SQLite похожа на таблицу в Excel с заранее заданными колонками. Агент добавил колонку в код, а файл базы у тебя на диске был создан раньше, без неё. Тесты этого не заметили: каждый раз они создают новую чистую базу, в которой колонка уже есть. Изменение структуры базы называется миграцией, и о ней агента нужно просить явно.
«Ошибка: table applications has no column named consent_at. Почини миграцией при старте бота, старые заявки не терять. Добавь тест на базу без этой колонки.»
Агент справился за 3,5 минуты и 13 ходов, тестов стало 12 из 12. Живая проверка: человек получил «Спасибо! Ваша заявка принята», владельцу пришло «Новая заявка #2» с полем «Согласие: 09.10.2026 14:08». Старая заявка #1 осталась на месте, дата согласия у неё пустая: она пришла до появления поля. Так и должно быть.
Правило на будущее: попросил агента новое поле в базе, сразу добавь «нужна миграция, старые данные не терять». Проверяй на своей рабочей базе со старыми заявками, а не на новой.
Шаг 4. Имя и телефон не храним на сервере
По решению из урока 2.2 бот держит в своей базе только номер заявки, курс и даты, а имя и телефон отправляет сразу в файл на Диске. В прогоне это было отдельное задание, и результат проверили выборкой из базы. Текст задания в формулировке курса:
«Не храни имя и телефон в локальной базе: только номер заявки, курс и даты. Имя и телефон сразу отправляй в файл на Диске. Удали имя и телефон из уже сохранённых строк базы. Покажи последние 5 строк базы.»
Строка про уже сохранённые записи важна: в базе остались заявки, которые ты отправлял в уроке 2.3 и в этом уроке. Без неё старые имена и телефоны так и лежат в файле базы и уедут на сервер.
Шаг 5. Потеря данных, которую не видят тесты
Самая дорогая ошибка прогона случилась уже на сервере, но правку делаем сейчас, до выкладки. Яндекс Диск на запрос «дай файл» отвечает не самим файлом, а переадресацией (код 302, «файл лежит по другому адресу»). Код агента за переадресацией не пошёл, решил, что файла нет, создал новый zayavki.xlsx с одной заявкой и загрузил поверх старого. Все прошлые заявки владельца исчезли.
Тесты были зелёными: в них настоящий Диск подменён заглушкой, а заглушка отвечает так, как её написали, без всяких переадресаций. Поймать такое можно только живой проверкой: две заявки подряд и взгляд в настоящий файл.
«Скачивание с Диска: иди за переадресацией (follow_redirects). Новый файл создавай только при ответе 404. При любой другой ошибке скачивания файл на Диск не загружай, а сохрани заявку в запасной pending.jsonl: только номер заявки, курс и время, без имени и телефона. Добавь тест: ошибка скачивания, значит загрузки нет.»
Агент управился за 5,2 минуты. Живая проверка: вторая заявка дописалась в файл, первая на месте. В самом прогоне запасной файл сначала остался без телефона, но с именем; это исправлено правкой из шага 1, поэтому здесь сразу просим без имени. Запомни правило целиком, оно пригодится в каждом продукте трека: не удалось прочитать данные, значит не перезаписывай их.
Шаг 6. Время по Москве и tzdata
В уроке 2.5 бот переедет на сервер, часы которого идут по UTC, и время согласия придётся брать по Москве через модуль zoneinfo и пояс Europe/Moscow. На Windows zoneinfo без пакета tzdata падает с ошибкой ZoneInfoNotFoundError. Строку tzdata в requirements.txt добавляет правка из шага 1; проверь, что она там есть.
Если не получилось
- Видишь: бот сначала спрашивает имя, а согласие в конце. Почему: старый порядок сценария. Что сделать: задание на перенос согласия из шага 1.
- Видишь: «Произошла ошибка при сохранении заявки», в терминале no column named … Почему: новое поле есть в коде, но не в файле базы. Что сделать: задание на миграцию из шага 3.
- Видишь: Диск отвечает 500 InternalServerError при повторной заявке или 403 при скачивании. Почему: у токена нет права чтения. Что сделать: отметь «Чтение всего Диска», получи новый токен по ссылке из шага 2, замени строку в .env, перезапусти бота.
- Видишь: права поменял, а 500 осталась. Почему: старый токен работает со старыми правами. Что сделать: новый токен по ссылке authorize.
- Видишь: в zayavki.xlsx осталась одна последняя заявка. Почему: код не прошёл переадресацию 302 и перезаписал файл. Что сделать: задание из шага 5; прошлые заявки ищи в уведомлениях владельца в Telegram.
- Видишь: на Windows ZoneInfoNotFoundError. Почему: нет пакета tzdata. Что сделать: строка tzdata в requirements.txt.
Контрольный вопрос. Первая заявка легла в zayavki.xlsx, а на второй Диск ответил 500 InternalServerError. Что проверяешь первым?
Подсказка: Чем первая заявка отличалась от второй с точки зрения Диска?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Контрольный вопрос. Агент добавил поле «источник заявки», тесты 12 из 12, а на твоей базе бот пишет «Произошла ошибка при сохранении заявки». Что попросишь у агента?
Подсказка: Почему тесты прошли, а твоя база нет?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Контрольный вопрос. Бот не смог скачать zayavki.xlsx: Диск ответил 503. Что по правилу урока должен сделать код?
Подсказка: Новый файл создаётся только при одном ответе Диска. Каком?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Задание. МИССИЯ «Две заявки подряд». Доведи бота до выгрузки на Яндекс Диск по шагам урока: согласие первым шагом, кнопка «Не согласен», токен с чтением и записью, миграция, имя и телефон вне базы, правило «не удалось скачать, значит не перезаписывать». Затем проверь вживую: отправь заявку, потом вторую с другим именем, потом нажми /start и сразу «Не согласен». Принято, если: приложен снимок zayavki.xlsx на Диске, где видны обе заявки с датой согласия; снимок чата, где после «Не согласен» бот ничего не спросил; снимок уведомления владельцу «Новая заявка» с полем «Согласие»; вывод последних строк базы без имён и телефонов; снимок итога тестов, где есть тест на ошибку скачивания; 2–3 предложения, какую ошибку ты встретил и какой фразой попросил агента её исправить. Телефоны тестовые, токены на снимках не видны.
Подсказка: Перед снимком файла проверь, что первая заявка не пропала после второй. Если пропала, это шаг 5.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Читайте также: Telegram-бот заявок и 152-ФЗ: где хранить данные клиентов
Частые вопросы
Нужно ли согласие на обработку персональных данных в Telegram-боте?
Да, если бот собирает имя и телефон: согласие — первым шагом, до сбора данных, с сохранением даты. Не юридическая консультация; текст согласия проверяет владелец бизнеса.
Где хранить заявки из Telegram-бота по 152-ФЗ?
На сервере в России: с 01.07.2025 запись и хранение персональных данных граждан РФ — только в базах на территории РФ (ч. 5 ст. 18, по открытым источникам — сверяйте с текстом закона). В курсе заявки уходят в CRM на сервере в Москве.
Почему бот потерял старые заявки?
В нашем прогоне 09.10.2026 ссылка на скачивание с Диска отдала перенаправление, код решил «файла нет» и записал новый файл поверх старого. Тесты с подменой Диска этого не поймали — нашла живая проверка второй заявкой.
Что дальше
Бот принимает заявку целиком и не теряет старые. Осталось главное для заказчика: чтобы бот отвечал ночью и в выходные, когда твой компьютер выключен. В следующем уроке выложим его на сервер.
Итог:
• согласие первым шагом, время нажатия записано по Москве;
• токен Яндекс Диска с чтением и записью, после смены прав новый токен;
• новое поле в базе всегда с миграцией, имени и телефона в базе нет;
• не удалось скачать файл, значит не перезаписывать; это ловит только живая проверка.
