Trigger.dev Arka Plan Görevinde CAPTCHA Nasıl Çözülür

Trigger.dev’de bir captcha çözümü ilk dağıtımda, kodla hiçbir ilgisi olmayan bir sebeple başarısız olur. Trigger.dev sizin uygulamanızı çağırmaz. Görevinizi bir Docker imajına derler ve kendi makinelerinde çalıştırır; bu yüzden bir görevin içindeki 127.0.0.1, çözücünüzün bulunduğu makine değil, o konteynerdir. Server modu bunu tek bir ayarla düzeltir. Doğru yapılması gereken ikinci şey maxDuration değeridir, çünkü çözücüyü yoklamakla geçen süre bu bütçeden tamamen düşer.
Neye ihtiyacınız var
- SDK’sı kurulu ve kök dizininde trigger.config.ts bulunan bir Trigger.dev projesi.
- Bir Windows makinesinde çalışan CapSkip ve dağıtılan imaja dahil olması için aynı projeye eklenmiş Node istemcisi.
- Koda sabitlenmek yerine görev yüküyle gelen sitekey ve sayfa URL’si; böylece tek bir görev her forma hizmet eder.
- Server modu ve çözücü için erişilebilir bir adres. Adım 1’deki sebepten ötürü Trigger.dev Cloud üzerinde bu isteğe bağlı değildir.
# npm install capskip npm install @trigger.dev/sdk capskip
Adım 1: görev gerçekte nerede çalışır ve bu hangi modu gerektirir
Çoğu platform rehberi bu konuyu sona bırakabilir. Bu rehber bırakamaz, çünkü geri kalan her şeyin çalışıp çalışmayacağını bu belirler. Trigger.dev’in dağıtım tanımı açıktır: kod bir Docker imajına paketlenir ve Trigger.dev örneğinize dağıtılır, her koşu da onların yönettiği yalıtılmış bir ortamda yürütülür. Göreviniz, editörünüzün bulunduğu yerde çalışmıyor.
Dolayısıyla run fonksiyonunun içindeki loopback adresi görev konteynerine çözümlenir. Orada 8080 portunu dinleyen hiçbir şey yoktur ve hata, her denemede NetworkException olarak yüzeye çıkan bir bağlantı reddidir.
İki bağlantı modu vardır. Local, 127.0.0.1 adresine bağlanır ve yalnızca o cihaza yanıt verir; otomasyonunuz ile çözücü aynı makineyi paylaştığında doğru seçim budur. Server ise ağ adresinize veya genel IP adresinize bağlanır; böylece başka bir makine, bir VPS ya da barındırılan bir platform aynı Windows makinesine API üzerinden ulaşabilir. Server modu yalnızca çözücünün hangi adresi dinlediğini değiştirir. Donanım yine sizin donanımınızdır ve çözüm başına ücretlendirme yine yoktur. Her iki mod da şurada yer alır: bağlantı ayarları.
| Trigger.dev’i nasıl çalıştırdığınız | Hangi bağlantı modu |
|---|---|
| CapSkip makinesinde dev CLI | Local modu. 127.0.0.1 gerçekten doğru |
| Kendi ağınızda, kendi barındırdığınız kurulum | Çözücünün LAN adresiyle Server modu |
| Trigger.dev Cloud | Statik bir genel IP ve bir güvenlik duvarı kuralı ile Server modu |
Üçüncü satır için sabit bir genel IP ayarlamakta fayda var; böylece adres, çalışan bir dağıtımın altından kaymaz. Adresi kaynak koda değil bir ortam değişkenine koyun, çünkü dev CLI ile dağıtılan görev farklı değerler ister.
// npm install capskip
import { CapSkip } from "capskip";
// 127.0.0.1 while the dev CLI runs it on your machine,
// the solver's reachable address once it is deployed.
export const solver = new CapSkip({
host: process.env.CAPSKIP_HOST ?? "127.0.0.1",
port: Number(process.env.CAPSKIP_PORT ?? 8080),
recaptchaTimeout: 120,
});Adım 2: maxDuration çözümü kapsamalıdır
Trigger.dev bir koşuyu saniye cinsinden maxDuration değerine göre ölçer ve belgelenmiş asgari değer beştir. İstisnalar açıkça sayılmıştır: wait.for, triggerAndWait ve batchTriggerAndWait içinde geçen süre sayılmaz. Beklenen bir HTTP isteği bu listede yoktur; dolayısıyla istemcinin çözücüyü yoklarken harcadığı her saniye tam olarak sayılır.
Bu önemlidir, çünkü çözmek büyük ölçüde beklemektir. Bir reCAPTCHA v2 çözümü genellikle on beş ila kırk beş saniye sürer ve yoğun bir kuyruk bunu daha da uzatabilir. maxDuration değerini görevin geri kalanına göre değil, çözümü de bütçeye katarak ayarlayın.
// trigger.config.ts sets the project-wide floor.
import { defineConfig } from "@trigger.dev/sdk";
export default defineConfig({
project: "proj_YOUR_PROJECT_REF",
maxDuration: 60,
});
// A solving task overrides it. 60s is not enough on its own:
// the solve alone can use most of that budget.
export const solveAndSubmit = task({
id: "solve-and-submit",
maxDuration: 300,
run: async (payload) => { /* ... */ },
});İstemcinin kendi üst sınırını bunun altında tutun ki önce istemci pes etsin ve okuyabileceğiniz bir hata fırlatsın. Node istemcisinin varsayılanı reCAPTCHA, Turnstile ve GeeTest için 300 saniye, görsel CAPTCHA’lar için 120 saniyedir. reCAPTCHA istemci zaman aşımını 120 saniyeye düşürmek, 300 saniyelik bir maxDuration altında, görevin token ile yapacağı iş için yer bırakır.
Adını koymaya değer bir tuzak var. wait.for, maxDuration hesabının dışında tutulduğu için bir görevi duraklatmanın bedelsiz yolu gibi görünür. Token açısından bedelsiz değildir. Bir reCAPTCHA token’ı duvar saatiyle yaklaşık iki dakika geçerlidir ve platformun saati ile token’ın saati farklı saatlerdir. Çözümü ve kullanımı kodun aynı bölümünde tutun; tasarımınızı bunun üzerine kurmadan önce bir reCAPTCHA token’ı ne kadar süre geçerli kalır başlıklı yazıyı bir kez okuyun.
Adım 3: yeniden denemeler ve hangi hatalar buna değer
Görevler varsayılan olarak üç kez yeniden denenir; üstel geri çekilmeyi factor, minTimeoutInMs, maxTimeoutInMs ve randomize ile yapılandırırsınız. CLI’nın ürettiği yapılandırma DEV ortamında yeniden denemeyi kapatır; production ortamında yeniden denenen bir görevin sizin makinenizde anında başarısız oluyormuş gibi görünmesinin sebebi budur.
Yeniden denenen bir görev run fonksiyonunun tamamını yeniden çalıştırır, yani yeniden çözüm yapar. Devralınacak bayat bir token yoktur ve bu da varsayılanları makul kılar. Asıl ayarlanmaya değer olan, hangi hataların yeniden deneme hakkı alacağıdır.
| Hangi istisna | Ne anlama geldiği | Yeniden denemeye değer mi? |
|---|---|---|
| NetworkException | CapSkip’e ulaşılamadı ya da yeniden başlıyor | Evet. Yeniden denemeler tam da bunun için |
| TimeoutException | Yoklama, istemcinin kendi üst sınırını aştı | Belki bir kez. Üç deneme nadiren mantıklıdır |
| ApiException | API bir hata kodu döndürdü | Hata koduna bağlı. Genellikle hayır |
| ValidationException | Parametreler yanlıştı ve yine yanlış olacak | Hayır. AbortTaskRunError fırlatın |
AbortTaskRunError denemeyi başarısız sayar ve yeniden denemeyi kapatır; hatalı biçimlendirilmiş bir isteğin hak ettiği de budur. Bozuk bir sitekey üçüncü denemede düzelmez ve doksan saniyelik üç deneme, bunu kanıtlamak için harcanan dört buçuk dakika demektir.
import { task, AbortTaskRunError } from "@trigger.dev/sdk";
import { ValidationException } from "capskip";
try {
const { code } = await solver.recaptcha(sitekey, pageUrl);
return await postForm(pageUrl, code);
} catch (err) {
// Wrong parameters will be wrong on all three attempts.
if (err instanceof ValidationException) {
throw new AbortTaskRunError(err.message);
}
throw err; // everything else takes the normal backoff
}Adım 4: görevin tamamı
Yukarıdaki her şey tek dosyada. İstemci modül kapsamında oluşturulur; böylece koşu başına değil konteyner başına bir kez kurulur ve koşuya özel hiçbir durum taşımaz.
// npm install capskip
import { task, AbortTaskRunError } from "@trigger.dev/sdk";
import { CapSkip, ValidationException } from "capskip";
const solver = new CapSkip({
host: process.env.CAPSKIP_HOST ?? "127.0.0.1",
port: 8080,
recaptchaTimeout: 120,
});
export const submitSignup = task({
id: "submit-signup",
maxDuration: 300,
retry: { maxAttempts: 3, minTimeoutInMs: 2000 },
queue: { concurrencyLimit: 10 },
run: async (payload, { ctx }) => {
const { sitekey, pageUrl, email } = payload;
try {
// Solve and submit together. The token is short lived.
const { code } = await solver.recaptcha(sitekey, pageUrl);
const res = await postSignup(pageUrl, email, code);
return { status: res.status, runId: ctx.run.id };
} catch (err) {
if (err instanceof ValidationException) {
throw new AbortTaskRunError(err.message);
}
throw err;
}
},
});Bu çağrı reCAPTCHA v2 içindir. Diğer türler de aynı biçimdedir: invisible veya enterprise değerini 1 yapın, ya da version değerini bir action ile birlikte v3 yapın, ya da bunun yerine turnstile veya geetest çağırın. Tüm yüzey için bakın: Node.js CAPTCHA çözücü sayfası.
Adım 5: eşzamanlılık ve asıl üst sınırın nerede olduğu
queue seçeneği, bir görevin aynı anda kaç koşusunun yürütüleceğini sınırlar. Çözüm başına ücret alan bir çözücüde bu sayı aslında bir harcama kontrolüdür ve insanlar bu yüzden düşük tutar. Burada ise tek bir makineye dair bir kapasite sorusudur; bu yüzden değeri bütçenizin kaldırabileceğine göre değil, çözücünün ve hedef sitenin kaldırabileceğine göre ayarlayın.
Sınırı gerçekte iki şey belirler: CapSkip’i çalıştıran Windows makinesi ve gönderim yaptığınız sitenin, sizi hız sınırlamasına almadan önce istekleri ne kadar hızlı kabul edeceği. Genellikle ikincisi daha dardır. Hiçbir iş bir bakiyenin arkasında sıraya girmez ve ay sonunda hiçbir şey çökmez.
export const submitSignup = task({
id: "submit-signup",
// Sized for the solver machine and the target site,
// not for a credit balance.
queue: { concurrencyLimit: 10 },
maxDuration: 300,
run: async (payload) => { /* ... */ },
});Sık görülen hatalar ve anlamları
| Gördüğünüz | Neden | Düzeltme |
|---|---|---|
| dev CLI ile çalışıyor, dağıtımdan sonra NetworkException | Dağıtılan görev bir konteynerdir, dolayısıyla loopback o konteynerdir | Server modu ve dağıtılan ortam için CAPSKIP_HOST ayarı |
| Koşu, çözümün ortasında durduruluyor | maxDuration, çözümün sürdüğü süreden kısa | Değeri görevde, istemcinin kendi zaman aşımının üzerine çıkarın |
| Görev DEV ortamında anında başarısız oluyor ama production ortamında yeniden deneniyor | Üretilen yapılandırma DEV ortamında yeniden denemeyi kapatıyor | Beklenen durum. Yeniden deneme davranışını dağıtılmış bir ortamda test edin |
| Üç deneme, aynı hata, birkaç dakika boşa gitti | Bir parametre hatası geçici sanılıyor | ValidationException için AbortTaskRunError fırlatın |
| Bir wait.for sonrasında token reddediliyor | Bekleme maxDuration için bedelsizdir, token için değildir | Çözümü beklemeden sonra, göndermeden hemen önce yapın |
| Bir ApiException içinde ERROR_WRONG_USER_KEY | Dağıtılan ortamda CAPSKIP_API_KEY tanımlı değil | Değeri Trigger.dev ortam değişkenlerinde ayarlayın, sonra yeniden dağıtın |
| Elle yazılmış bir yoklamadan CAPCHA_NOT_READY | Sonuç, tamamlanmadan önce okundu | Yoklamayı istemciye bırakın. Kendiliğinden geri çekilir |
Son yanıt tam da göründüğü gibi yazılır; eksik harf bizim tarafımızdaki bir yazım hatası değildir, çünkü API gerçekten de onu bu şekilde döndürür. Tamamı şurada açıklanıyor: CAPCHA_NOT_READY rehberi.
FAQ
Trigger.dev Cloud görevi masamdaki bir çözücüye ulaşabilir mi?
Evet, Server moduyla. Görev, Trigger.dev’in yönettiği bir konteynerde çalışır; dolayısıyla oradaki loopback o konteynerdir. Bağlantı ayarları altında CapSkip’i genel IP adresinize bağlayın, önüne yalnızca beklediğiniz adreslere izin veren bir güvenlik duvarı kuralı koyun ve Trigger.dev ortam değişkenlerinde CAPSKIP_HOST değerini ayarlayın. Adresin altınızdan kaymaması için sabit bir genel IP önerilir.
Trigger.dev’i kendi sunucumda barındırmak bunların herhangi birini değiştirir mi?
Modeli değil, adresi değiştirir. Kendi sunucunuzda barındırılan koşular da uygulamanızı çağırmak yerine kodunuzu örnek üzerindeki konteynerlerde çalıştırır; yani loopback yine konteynerdir. Fark şu: örnek genellikle kendi ağınızdadır, dolayısıyla Server modu genel bir adres yerine bir LAN adresi kullanabilir ve hiçbir güvenlik duvarı kuralının internete bakması gerekmez.
Çözüm, diğer görevlerin çağırdığı ayrı bir görev olmalı mı?
Genellikle hayır. Ayırmak, token’ın bir görev sınırını geçmesi ve üst görev devam ederken bir yükün içinde beklemesi demektir; bu da süresi çoktan dolmuş bir token’ı harcamanın en hızlı yoludur. Çözümü ve token’ı tüketen işi aynı run fonksiyonunda tutun ve kimlik bilgisini değil, sonucu döndürün. Ayrı bir görev yalnızca döndürdüğü şey token olmadığında mantıklıdır.
Bunu Inngest’te yapmaktan farkı ne?
Dağıtım modeli tam tersidir ve bu, cevabın tamamını değiştirir. Inngest uygulamanızı HTTP üzerinden çağırır; yani kodunuz onu nereye dağıttıysanız orada çalışır ve bağlantı modu sizin kendi barındırmanızla ilgili bir sorudur. Trigger.dev ise kodunuzu kendi makinelerinde çalıştırır; bu yüzden bulut sürümünde Server moduna sizin yerinize karar verilmiş olur. Orada bir çözümün neden tek bir adımın içinde durması gerektiği dahil, Inngest sürümü şurada: Inngest rehberi.
Kısa özet
CapSkip’i Server modunda çalıştırın ve CAPSKIP_HOST değerini Trigger.dev ortamında ayarlayın; çünkü dağıtılan bir görev bir konteynerdir ve oradaki loopback o konteynerdir. Göreve çözümü kapsayan bir maxDuration verin, çünkü bir HTTP uç noktasını yoklamak istisna tutulan beklemelerden biri değildir. İstemcinin zaman aşımını bunun altında tutun. Varsayılan üç yeniden denemeyi olduğu gibi bırakın, ancak parametre hataları için AbortTaskRunError fırlatın. Çözümü ve gönderimi aynı run fonksiyonunda yapın; asla bir beklemenin ya da görev sınırının iki yakasına dağıtmayın.
- İstemcinin arkasındaki ham uç noktalar şurada belgelenmiştir: CapSkip API dokümantasyonu.
- Onay kutusu doğrulamasının kendisi şurada anlatılıyor: reCAPTCHA v2 çözücü sayfası.
Bir eşzamanlılık sınırı seçmeden önce tartmakta fayda var: CapSkip, bir captcha çözücü olarak zaten sahip olduğunuz donanımda çalışır; dolayısıyla seçtiğiniz sayı bir bütçe kararı değil, bir kapasite kararıdır.
