scripts/utils/captcha.ts

87 lines
2.2 KiB
TypeScript

import { sleep } from '@fuman/utils'
import { z } from 'zod'
import { ffetch } from './fetch.ts'
import { getEnv } from './misc.ts'
const CreateTaskResponse = z.object({
errorId: z.number(),
errorCode: z.string().optional().nullable(),
taskId: z.number(),
})
const GetTaskResultResponse = z.object({
errorId: z.number(),
errorCode: z.string().optional().nullable(),
status: z.enum(['ready', 'processing']),
solution: z.unknown().optional(),
})
export async function solveCaptcha(task: unknown) {
const res = await ffetch.post('https://api.capmonster.cloud/createTask', {
json: {
clientKey: getEnv('CAPMONSTER_API_TOKEN'),
task,
},
}).parsedJson(CreateTaskResponse)
if (res.errorId) {
throw new Error(`createTask error ${res.errorId}: ${res.errorCode}`)
}
const taskId = res.taskId
await sleep(5_000)
let requestCount = 0
while (true) {
requestCount += 1
if (requestCount > 100) {
// "Limit: 120 requests per task. If the limit is exceeded, the user's account may be temporarily locked."
// just to be safe
throw new Error('captcha request count exceeded')
}
const res = await ffetch.post('https://api.capmonster.cloud/getTaskResult', {
json: {
clientKey: getEnv('CAPMONSTER_API_TOKEN'),
taskId,
},
}).parsedJson(GetTaskResultResponse)
if (res.errorId) {
throw new Error(`getTaskResult error ${res.errorId}: ${res.errorCode}`)
}
if (res.status === 'ready') {
return res.solution
}
await sleep(2_000)
}
}
export async function solveRecaptcha(params?: {
url: string
siteKey: string
s?: string
userAgent?: string
cookies?: string
isInvisible?: boolean
}) {
const res = await solveCaptcha({
type: 'RecaptchaV2TaskProxyless',
websiteURL: params?.url,
websiteKey: params?.siteKey,
recaptchaDataSValue: params?.s,
userAgent: params?.userAgent,
cookies: params?.cookies,
isInvisible: params?.isInvisible,
})
if (typeof res !== 'object' || !res || !('gRecaptchaResponse' in res) || typeof res.gRecaptchaResponse !== 'string') {
throw new Error('invalid recaptcha response')
}
return res.gRecaptchaResponse
}