RUEN
Обсудить проект

Инженерный кейс

Продавец на площадке, у которой нет API

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

состояний у заказа
9
способа пересоздать сессию
3
работы без связи с лицензией
6 ч

Бот ведёт витрину продавца на торговой площадке: принимает заказ, спрашивает у покупателя данные, покупает подписку у поставщика и отчитывается о результате. Ни у площадки, ни у поставщика машинного интерфейса нет — всё идёт тем же путём, что у обычного посетителя, через антибот-защиту.

Заказ узнаётся по случайной метке в описании, а не по названию

Панель сама создаёт лот и дописывает в конец описания короткую случайную метку. При новом заказе бот достаёт описание и ищет её.

Сначала сопоставляли по словам в названии — «месяц», «30 дней». Продавец правит названия: добавляет эмодзи, меняет формулировку под сезон, и сопоставление разваливается молча. Метка живёт в описании, а не в названии, по той же причине: в названии она занимает место в выдаче, и продавец её выкусывает — код даже принудительно вырезает её оттуда при обновлении.

Успехом покупки считается запись в истории поставщика, а не код ответа

Перед покупкой бот считает, сколько покупок числится на этого получателя. После «успешного» ответа ждёт и считает снова. Не выросло — значит покупки не было.

У поставщика нет ни интерфейса, ни договорённости о кодах ответа: страница возвращает успех и когда покупка не прошла — например, не хватило средств. Доверять ответу нельзя, поэтому проверяется следствие, а не обещание.

Проверку решают снаружи, но отпечаток соединения должен совпасть

Внешний сервис проходит антибот-проверку и отдаёт пропуск вместе с описанием клиента. Бот собирает соединение ровно с этим описанием и подменяет отпечаток под тот же браузер.

Пропуск привязан к отпечатку клиента. Обычная библиотека запросов имеет узнаваемый отпечаток, и защита отклоняет запрос с действительным пропуском сразу — то есть пройти проверку, не подставив тот же отпечаток, бесполезно. Профиль браузера при этом выбран не самый свежий: в коде записано, что новые отпечатки защита отбивает чаще.

Между внешним решателем и сайтом стоит собственный прокси

Схема: работники внешнего сервиса → прокси на нашем сервере → канал провайдера → сайт. Сам бот идёт в тот же канал напрямую.

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

Сессия пересоздаётся тремя способами, и выбор способа — отдельное решение

Есть три режима: переиспользовать текущую, пересобрать соединение не проходя проверку заново, и пройти проверку заново.

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

Вход к поставщику идёт через код, который уходит продавцу в чат

Полный путь: пройти защиту, открыть магазин, запросить вход по коду, отправить код картинкой продавцу и опрашивать состояние, пока тот не подтвердит с телефона.

У поставщика нет ни интерфейса, ни пары логин-пароль — вход только через подтверждение с телефона. Автоматизировать это до конца нельзя в принципе, поэтому человек встроен в поток осознанно, в одном месте и с понятным действием.

Состояние заказа — автомат на диске, а не в памяти

Девять состояний, два указателя, файл на диске, все изменения под замком. После перезапуска незавершённые заказы поднимаются и разбираются по возрасту: ожидающие ввода истекают по одному сроку, застрявшие в покупке — по другому.

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

Частичный успех — отдельный исход, а не «ошибка» и не «успех»

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

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

Возврат, сделанный руками, отменяет выдачу — но не всякую

Смена статуса на возврат снимает заказ с очереди. Исключение — заказы, которые уже выполнены или прямо сейчас покупаются.

Без этого продавец возвращал деньги руками, а бот через минуту всё равно выдавал товар — двойной убыток. Но отменять заказ в середине покупки нельзя: деньги у поставщика уже потрачены.

Истёкшая авторизация — пауза с кнопкой, а не провал заказа

Если сессия у поставщика протухла, заказ уходит в ожидание: покупателю — нейтральное «техническая пауза», продавцу — оповещение с кнопкой «повторить».

Истёкшая сессия — не отказ поставщика и не ошибка покупателя, а задача продавца на полминуты. Автоматический возврат денег сжигал бы продажу из-за ситуации, которая чинится одним нажатием. Подробности ошибок покупателю не показываются вовсе — наружу идёт нейтральный текст, техника уходит в оповещение.

Клиенту отгружается байткод, а не исходники

Сборка компилирует проект, кладёт в архив только разрешённый список файлов и перекомпилирует уже на целевом сервере.

Бот содержит рабочие обходы защиты и логику покупки за реальные деньги: раздача исходников означает мгновенное растекание. Компиляция на целевой машине нужна потому, что байткод привязан к версии интерпретатора.

Python · curl_cffi · Telegram Bot API · Docker