Captcha: явные задания в JavaScript
Через ctx.captcha() можно узнать параметры задания, оценить стоимость, явно отправить его и получить ответ. Этот API не открывает сайт, не обнаруживает новую CAPTCHA, не заполняет форму и не применяет ответ в браузере. Тип задания определяет входные поля и формат ответа. В преобразованиях выгрузки, где запрещены host calls, эти методы недоступны.
Контракт одинаков в облаке и на собственном агенте. В сценарии не нужны внешний аккаунт, ключ сервиса или платёжный токен. Работа учитывается по тарифу и балансу пространства сайта. Агенту требуется связь с Akmelo; без облака эти операции недоступны.
Перед отправкой
Начните с types() и describe(type): они читают актуальный каталог без платного задания. После подготовки полей вызовите quote(task). Задание создаёт только createTask(); в Python используется create_task(). Оценка стоимости сама по себе не резервирует деньги и не фиксирует будущую цену. Для создания CAPTCHA должна быть разрешена настройками сайта и платформы.
Не отправляйте пароли, cookies авторизации, персональные данные и постороннее содержимое страницы. Передавайте только поля из describe(type). Произвольные адреса API, выбор внешнего сервиса, ключи и callback не являются настройками задания. Разным типам нужны разные данные; их нельзя просто переименовать друг в друга.
В GridTask.previousId передавайте наш taskId предыдущего готового GridTask того же сайта и пространства, а на агенте — также того же агента. Платформа сама разрешает внутреннюю ссылку; внешний идентификатор или чужое задание отклоняются до отправки.
Методы
types()describe()quote()createTask()getTaskResult()waitForResult()reportCorrect()reportIncorrect()
types
await ctx.captcha().types()
Возвращает {types: [...], languages: [...], unit: 1000000}. Каждый элемент каталога описывает доступный тип; languages содержит допустимые коды языковых пулов. Список актуален для этой установки: не храните собственную копию названий как источник истины. Включённость тарифа и остатки проверяются отдельно при создании.
describe
await ctx.captcha().describe('ImageToTextTask')
Возвращает описание одного типа: type, name, kind, parameters, solution и ценовую информацию. parameters содержит допустимые поля, типы, обязательность и пределы; solution описывает форму ответа. Неизвестный тип отклоняется. Например, ImageToTextTask принимает base64 в body, а AudioTask — аудио в body и язык аудио в lang (это отдельное поле от options.languagePool); точные ограничения читайте в схеме.
В некоторых описаниях есть required_any: кроме обычных обязательных полей нужно заполнить хотя бы один полный альтернативный набор. requirements описывает условные требования, например для разных версий задания. Одного списка необязательных полей недостаточно для правильной отправки. В price значение quoted: false означает технический резерв вместо подтверждённого диапазона цены; available говорит о доступности цены, а не об успешном доступе к сайту.
quote
await ctx.captcha().quote(task)
Проверяет поля и возвращает {minimum, maximum, unit: 1000000}. Суммы в микро₽: 1 000 000 единиц = 1 ₽. minimum: null означает, что нижняя граница не определена; это не нулевая цена. maximum — верхняя оценка для предварительного резерва. Включённые в тариф задания и разрешённые дополнительные расходы всё равно проверяются сервером.
createTask
await ctx.captcha().createTask(task, options = {})
Создаёт явное задание. Результат: {taskId, status}, где taskId — UUID платформы, при новой отправке статус processing либо uncertain. Повтор с тем же ключом запроса может вернуть уже готовое или неуспешное состояние ready/failed. Не передавайте сюда идентификатор задания из другой системы. Неизвестные типы, поля и некорректные значения отклоняются до отправки. Возврат uncertain означает неопределённость отправки: сохраните идентификатор и проверяйте его, а не создавайте новый платный запрос.
getTaskResult
await ctx.captcha().getTaskResult(taskId)
Читает состояние существующего задания, не создавая новое. Возвращает taskId, status, unit и, когда доступны, solution, cost, error. Формат solution зависит от типа: это JSON-значение, в том числе объект, массив, строка, число, логическое значение или null. Не считайте наличие текстового поля обязательным: ответ может содержать координаты, угол, токен или другую описанную структуру. Сверяйтесь с JSON Schema solution в описании типа. При ошибке используйте безопасное поле error для диагностики.
waitForResult
await ctx.captcha().waitForResult(taskId, timeoutMs = 120000)
Ожидает результат существующего задания. timeoutMs от 0 до 120000; 0 даёт однократную проверку. По окончании ожидания возвращается последнее состояние, в том числе processing или uncertain. Это не отменяет задание и не создаёт повтор. Остановка или общий срок прогона прерывают ожидание ошибкой. Чтобы продолжить позже, сохраните taskId и вызовите получение результата снова.
reportCorrect
await ctx.captcha().reportCorrect(taskId)
Отправляет обратную связь об уже проверенном правильном ответе. Возвращает {taskId, accepted}. accepted: true означает приём отзыва, а не автоматическую проверку сайта. Не отправляйте положительный отзыв просто потому, что статус стал ready.
reportIncorrect
await ctx.captcha().reportIncorrect(taskId)
Отправляет обратную связь об уже проверенном неправильном ответе. Возвращает {taskId, accepted}. Приём отзыва не обещает возврат денег, не отменяет задание и не запускает новое. Отказ сайта по другой причине не доказывает ошибку результата CAPTCHA.
Параметры создания
Ключи options одинаковы во всех языках; snake_case в Python относится к названиям методов.
| Поле | Значение | Для чего |
|---|---|---|
requestId |
1–128 символов: A–Z, a–z, 0–9, ., _, :, - |
Постоянный ключ идемпотентности одной логической отправки в пределах того же прогона. При её повторе используйте тот же ключ; для действительно нового входа нужен новый. В новом прогоне прежний ключ не переиспользует старую отправку: читайте сохранённый taskId, а не создавайте её снова. |
language |
ru или en |
Короткая форма выбора языкового пула. |
languagePool |
Код из types().languages, например zh |
Языковой пул инструкций задания. Это не язык сайта; не сочетайте его с отличающимся значением language. |
maxCost |
Неотрицательное целое в микро₽ | Ваш потолок на задание. Если системный резерв выше него, отправка отклоняется. Параметр не меняет цену задания. |
Статусы и оплата
| Статус | Что делать |
|---|---|
processing |
Ожидать по этому идентификатору. |
ready |
Читать ответ и отдельно проверять его применение. |
failed |
Разобраться в ошибке, не запускать бесконечный повтор. |
uncertain |
Отправка либо её итог не подтверждены. Повторно проверить этот же идентификатор. |
Готовый ответ может быть платным, даже если сайт его отверг. Действуют обычные для пространства количество CAPTCHA в тарифе, баланс, резервы и пределы стоимости. Если поле cost есть, это сумма списания пространства в микро₽, а не отдельный счёт другого сервиса. Оценка не равна итоговому списанию; неиспользованный резерв освобождается после сверки. Остановка прогона либо таймаут ожидания не обещают отмену или возврат.
Идентификатор доступен только тому же сайту и пространству. Агент читает только задания своего агента. Другой прогон того же разрешённого сайта может прочитать существующее задание; знание UUID не даёт доступа. Ответ хранится 24 часа. После истечения срока ready может вернуться без solution и с error: result_expired; сохраняйте нужный результат обработки, а не материал для повторной авторизации.
Полный пример со своей фикстурой
Подготовьте небольшую картинку с известным текстом и передайте только её base64 в параметре прогона fixture_image_base64, без URL и префикса data-URL. По умолчанию пример только получает оценку: send_task=false. Устанавливайте send_task=true, только когда хотите отправить платное задание. При смене фикстуры меняйте ключ запроса. Проверка кода проверяет синтаксис и SDK, не отправляя задание.
async function run(ctx) {
const captcha = ctx.captcha();
const body = ctx.param('fixture_image_base64', '');
if (!body) ctx.abort('Укажите base64 собственного тестового изображения');
const task = { type: 'ImageToTextTask', body };
const price = await captcha.quote(task);
ctx.log('Максимальный резерв: ' + price.maximum + ' микро₽');
if (!ctx.param('send_task', false)) return;
const created = await captcha.createTask(task, {
requestId: 'fixture-image-001',
language: 'ru',
maxCost: price.maximum,
});
const result = await captcha.waitForResult(created.taskId, 120000);
const solution = result.solution;
if (result.status === 'ready' && solution !== null &&
typeof solution === 'object' && !Array.isArray(solution) &&
typeof solution.text === 'string') {
ctx.emit({ text: solution.text }, 'fixture-001');
} else {
ctx.log('Состояние задания: ' + result.status);
}
}
Другие входные данные и ошибки
Для аудио сначала прочитайте describe("AudioTask"): передайте аудио base64 в body и документированное значение lang. Для остальных типов читайте их собственные параметры и схему ответа. Не отправляйте токеновое задание как картинку и не превращайте массив координат в строку. Некорректный base64, лишние поля, размер, отключённая функция, недостаточный бюджет и недоступный идентификатор — разные ошибки. Исправляйте причину; ожидание по имеющемуся UUID отличается от новой отправки.
Этот API не расширяет список типов, автоматически определяемых браузером. autoCaptcha=false (Python: auto_captcha=False) управляет автоматическим HTTP/браузерным режимом и не отключает явное создание. Чтобы исключить платные отправки, не вызывайте метод создания.