(() => { function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } window.alert = function(msg) { console.warn("차단된 alert:", msg); }; if (window.__zzimAutomationRunning) { console.warn("이미 실행 중인 자동화 스크립트가 있습니다."); return; } window.__zzimAutomationRunning = true; let isRunning = true; document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { isRunning = false; console.log("찜하기 자동화 중단됨 (ESC)"); } }); function getStoreName() { const el = document.querySelector('.KasFrJs3SA'); return el ? el.innerText.trim() : "스토어"; } function waitForZzimButtons(callback) { const check = () => { const buttons = document.querySelectorAll('button.zzim_button[type="button"]'); if (buttons.length > 0) { callback(buttons); } else { requestAnimationFrame(check); } }; requestAnimationFrame(check); } async function waitForButtonToChange(btn, timeout = 1000) { const start = Date.now(); while (Date.now() - start < timeout) { const pressed = btn.getAttribute("aria-pressed"); const label = btn.innerText?.trim(); if (pressed === "true" || (label && !label.includes("찜하기"))) { return true; } await sleep(50); } return false; } function createProgressUI() { const ui = document.createElement("div"); ui.id = "zzim-progress-ui"; ui.style.position = "fixed"; ui.style.bottom = "20px"; ui.style.right = "20px"; ui.style.background = "#222"; ui.style.color = "#fff"; ui.style.padding = "10px 15px"; ui.style.borderRadius = "8px"; ui.style.zIndex = "99999"; ui.style.fontSize = "14px"; ui.style.whiteSpace = "pre-line"; ui.style.boxShadow = "0 0 5px rgba(0,0,0,0.5)"; ui.innerText = "찜하기 자동화 시작..."; document.body.appendChild(ui); return ui; } async function runAutomation(startPage, finalPage) { const url = new URL(window.location.href); const storeName = getStoreName(); let currentPage = parseInt(url.searchParams.get("page") || startPage); let totalClicked = 0; const progressUI = createProgressUI(); const today = new Date().toISOString().split("T")[0]; if (currentPage > finalPage) { progressUI.innerText = `${storeName} 페이지 ${finalPage} / ${finalPage} 찜 완료: 0개 완료`; alert(`${storeName} 작업 이미 완료됨`); return; } while (isRunning && currentPage <= finalPage) { progressUI.innerText = `${storeName} 페이지 ${currentPage} / ${finalPage} 찜 완료: ${totalClicked}개 진행중`; await new Promise(resolve => waitForZzimButtons(resolve)); const buttons = Array.from(document.querySelectorAll('button.zzim_button[type="button"]')); for (let i = 0; i < buttons.length; i++) { const btn = buttons[i]; if (!isRunning) break; const pressed = btn.getAttribute("aria-pressed"); const label = btn.innerText?.trim(); if (pressed === "false" && label.includes("찜하기")) { try { btn.click(); const changed = await waitForButtonToChange(btn); if (changed) { totalClicked++; } progressUI.innerText = `${storeName} 페이지 ${currentPage} / ${finalPage} 찜 완료: ${totalClicked}개 진행중`; await sleep(200); } catch (e) { console.warn("❌ 클릭 실패:", e); } } } if (!isRunning || currentPage >= finalPage) break; const nextPage = currentPage + 1; url.searchParams.set("page", nextPage); await sleep(1000); window.location.href = url.toString(); setTimeout(() => document.dispatchEvent(new KeyboardEvent("keydown", {key: "Enter"})), 1500); return; } // 🔒 루프 종료 후 항상 최종 처리 const now = new Date(); const timestamp = now.getFullYear() + "-" + String(now.getMonth()+1).padStart(2, '0') + "-" + String(now.getDate()).padStart(2, '0') + " " + String(now.getHours()).padStart(2, '0') + ":" + String(now.getMinutes()).padStart(2, '0'); progressUI.innerText = `${storeName} 페이지 ${currentPage} / ${finalPage} 찜 완료: ${totalClicked}개 완료`; alert(`${storeName}\n${timestamp}\n총 ${totalClicked}개 찜 완료`); } chrome.storage.local.get(["isZzimRun", "pageCount", "_zzimStartPage", "_zzimFinalPage"], (res) => { if (!res.isZzimRun) return; // 제거는 완료 시점에만 수행됨 const url = new URL(window.location.href); const current = parseInt(url.searchParams.get("page") || "1"); if (!res._zzimStartPage || !res._zzimFinalPage) { const startPage = current; const finalPage = startPage + (res.pageCount || 1) - 1; chrome.storage.local.set({ _zzimStartPage: startPage, _zzimFinalPage: finalPage }, () => { runAutomation(startPage, finalPage); }); } else { runAutomation(res._zzimStartPage, res._zzimFinalPage); } }); })();