From e19c08b725b69eb2a8ba927fcee0dbeb569087ea Mon Sep 17 00:00:00 2001 From: hyoseung930 <35983843+hyoseung930@users.noreply.github.com> Date: Tue, 19 May 2026 18:14:22 +0900 Subject: [PATCH] New task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI/CD 배포 시작됐습니다. **핵심 변경:** - 쿠폰 1개 = 캡차 요청 **정확히 1번** (재시도 완전 제거) - OCR이 틀려도 바로 실패 처리 → TOO FREQUENT 원천 차단 - 쿠폰 간 5초 딜레이 유지 **트레이드오프:** OCR이 틀리면 해당 쿠폰은 실패로 표시됩니다. 그럴 경우 다시 "쿠폰 받기" 버튼 누르면 됩니다 (이미 성공한 쿠폰은 `alreadySucceeded` 로 스킵하고, 실패한 것만 재시도). --- back/src/wos/wos.service.ts | 64 +++++++++++++------------------------ 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/back/src/wos/wos.service.ts b/back/src/wos/wos.service.ts index 470a62f..4112dae 100644 --- a/back/src/wos/wos.service.ts +++ b/back/src/wos/wos.service.ts @@ -112,50 +112,30 @@ export class WosService { private async redeemOne( fid: string, code: string, - maxRetries = 5, ): Promise<{ success: boolean; message: string; err_code?: number }> { - for (let attempt = 1; attempt <= maxRetries; attempt++) { - if (attempt > 1) { - await new Promise((r) => setTimeout(r, 3000)); - } - - let captchaCode: string; - try { - captchaCode = await this.solveCaptcha(fid); - } catch (err: any) { - const msg: string = err.message || ''; - if (msg.includes('TOO FREQUENT') && attempt < maxRetries) { - this.logger.warn(`[${fid}] 캡차 TOO FREQUENT (${attempt}회), 10초 대기 후 재시도`); - await new Promise((r) => setTimeout(r, 10000)); - continue; - } - return { success: false, message: `캡차 오류: ${msg}` }; - } - - const ts = this.now(); - const giftSign = this.makeGiftSign(fid, ts, code, captchaCode); - const response = await axios.post( - `${WOS_API_BASE}/gift_code`, - new URLSearchParams({ fid, time: String(ts), sign: giftSign, cdk: code, captcha_code: captchaCode }), - { headers: { ...COMMON_HEADERS } }, - ); - - const data = response.data; - this.logger.log(`[${fid}] ${code} attempt=${attempt} captcha=${captchaCode} → code=${data.code} err=${data.err_code} msg=${data.msg}`); - - if (data.code === 0) return { success: true, message: SUCCESS_MSG }; - if (data.err_code === 40103) { - if (attempt < maxRetries) continue; - return { success: false, message: `캡차 인식 실패 (${maxRetries}회 시도)`, err_code: data.err_code }; - } - if (data.code !== 0 && (data.err_code === 0 || data.msg === 'Sign Error')) { - if (attempt < maxRetries) continue; - return { success: false, message: 'Sign Error (캡차 오인식)', err_code: data.err_code }; - } - return { success: false, message: data.msg || `실패 (${data.err_code})`, err_code: data.err_code }; + let captchaCode: string; + try { + captchaCode = await this.solveCaptcha(fid); + } catch (err: any) { + return { success: false, message: `캡차 오류: ${err.message}` }; } - return { success: false, message: '알 수 없는 오류' }; + const ts = this.now(); + const giftSign = this.makeGiftSign(fid, ts, code, captchaCode); + const response = await axios.post( + `${WOS_API_BASE}/gift_code`, + new URLSearchParams({ fid, time: String(ts), sign: giftSign, cdk: code, captcha_code: captchaCode }), + { headers: { ...COMMON_HEADERS } }, + ); + + const data = response.data; + this.logger.log(`[${fid}] ${code} captcha=${captchaCode} → code=${data.code} err=${data.err_code} msg=${data.msg}`); + + return { + success: data.code === 0, + message: data.code === 0 ? SUCCESS_MSG : (data.msg || `실패 (${data.err_code})`), + err_code: data.err_code, + }; } async redeemAll(fid: string) { @@ -291,7 +271,7 @@ export class WosService { this.logger.error(`[${user.fid}] ${code} 지급 실패: ${err.message}`); } - await new Promise((r) => setTimeout(r, 1500)); + await new Promise((r) => setTimeout(r, 5000)); } }