SearchTrademark/21. 찜하기_크롬 확장프로그램/content.js

167 lines
5.1 KiB
JavaScript

(() => {
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);
}
});
})();