87 lines
2.2 KiB
TypeScript
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
|
|
}
|