Files
custom-launcher/js/main.js
T
2026-06-08 02:33:14 +09:00

317 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const shortcut = document.getElementById('findMyShortcut');
let failIndex = 0;
const iconItems = [
{
id: 'findmydevice',
img: '/custom/launcher/img/find-my.svg',
label: '나의 찾기',
onclick: () => {
requireAccess(() => location.href = '/findmydevice');
}
},
{
id: 'wakeonlan',
img: '/custom/launcher/img/wakeonlan-svgrepo-com.svg',
label: 'PC 켜기',
onclick: () => {
fetch('/custom/wakeonlan/api.php?status=1')
.then(res => res.ok ? res.json() : Promise.reject())
.then(data => {
if (!data || data.result !== 'ok') {
showAlert('error');
return;
}
if (data.state === 'on') {
showAlert('wakeonlan_already');
return;
}
fetch('/custom/wakeonlan/api.php', { method: 'POST' })
.then(res => res.ok ? res.json() : Promise.reject())
.then(() => {
showAlert('wakeonlan_request_sent');
let attempts = 0;
const interval = setInterval(() => {
attempts++;
fetch('/custom/wakeonlan/api.php?status=1')
.then(res => res.ok ? res.json() : Promise.reject())
.then(status => {
if (status.state === 'on') {
clearInterval(interval);
showAlert('wakeonlan_success');
}
else if (attempts >= 12) {
clearInterval(interval);
}
})
.catch(() => clearInterval(interval));
}, 5000);
})
.catch(() => showAlert('error'));
})
.catch(() => showAlert('error'));
}
},
{
id: 'poke',
img: '/custom/launcher/img/R1280x0.jpg',
label: '콕 찌르기',
onclick: () => {
showAlert('poke_completed');
const alertBox = document.querySelector('#poke_completed');
const content = alertBox.querySelector('.alert-content');
content.style.minHeight = 'auto';
content.style.padding = '15px 20px 10px 20px';
content.style.justifyContent = 'flex-start';
const renderMain = (countText = "조회 중...") => {
content.innerHTML = `
<div id="poke_container" style="width:100%; display:flex; flex-direction:column; gap:6px;">
<p id="poke_title" style="text-align:center; font-weight:bold; margin-bottom:4px; color:#007AFF; font-size:1.05em;">${countText}</p>
<input type="text" id="poke_sender" placeholder="보내는 이 (미입력 시 익명)"
style="padding:8px 12px; border:1px solid #eee; border-radius:10px; outline:none; font-size:14px; background:#f9f9f9;">
<input type="text" id="poke_message" placeholder="할 말 (선택 사항)"
style="padding:8px 12px; border:1px solid #eee; border-radius:10px; outline:none; font-size:14px; background:#f9f9f9;">
<div class="button-custom-container" style="margin-top:6px; gap:6px;">
<button class="button-custom" id="btn_poke_cancel" style="background-color:#eee; color:#666; flex:1; min-width:auto;">닫기</button>
<button class="button-custom" id="btn_poke_confirm" style="flex:2; min-width:auto;">콕 찌르기</button>
</div>
<p id="poke_status" style="text-align:center; font-size:10px; color:#bbb; margin-top:2px; height:1px;"></p>
</div>
`;
document.getElementById('btn_poke_cancel').onclick = () => { closeAlert('poke_completed'); };
document.getElementById('btn_poke_confirm').onclick = handlePoke;
};
fetch('/custom/launcher/api.php?pokeCount=0')
.then(res => res.json())
.then(data => {
const n = Number(data.count) || 0;
document.getElementById('poke_title').innerHTML = `현재까지 <span style="color:#ff3b30;">${n}회</span> 찔렸습니다!`;
})
.catch(() => {
document.getElementById('poke_title').innerText = "현재 횟수를 불러올 수 없음";
});
const handlePoke = () => {
const sender = document.getElementById('poke_sender').value.trim() || "익명";
const message = document.getElementById('poke_message').value.trim() || "";
const titleField = document.getElementById('poke_title');
const statusField = document.getElementById('poke_status');
const confirmBtn = document.getElementById('btn_poke_confirm');
confirmBtn.disabled = true;
statusField.innerText = "전송 중...";
const params = new URLSearchParams({ sender, message });
fetch(`https://ha.chaegeon.com/api/webhook/jczSPJyIX4iG4JTvy9HztFq91MsrIfsUErFh4DLstDcsm7RKL?${params.toString()}`, {
method: 'POST',
mode: 'no-cors'
});
fetch(`/custom/launcher/api.php?pokeCount=1&sender=${encodeURIComponent(sender)}`)
.then(res => res.json())
.then(data => {
const n = Number(data.count);
titleField.innerHTML = `현재까지 <span style="color:#ff3b30;">${n}회</span> 찔렸습니다!`;
statusField.innerText = "성공!";
setTimeout(() => {
confirmBtn.disabled = false;
statusField.innerText = "";
}, 300);
})
.catch(() => {
statusField.innerText = "오류 발생";
confirmBtn.disabled = false;
});
};
renderMain();
}
},
{
id: 'car',
img: '/custom/launcher/img/car.png',
label: '니모',
onclick: () => {
requireAccess(() => location.href = 'https://seo.chaegeon.com/car/monitor.php');
}
}
];
function renderIcons() {
shortcut.className = 'find-container';
shortcut.innerHTML = '';
iconItems.forEach(item => {
const div = document.createElement('div');
div.className = 'iconContainer';
div.id = item.id;
if (item.onclick) div.addEventListener('click', item.onclick);
const img = document.createElement('img');
img.src = item.img;
img.alt = item.label;
const label = document.createElement('div');
label.className = 'iconLabel';
label.textContent = item.label;
div.appendChild(img);
div.appendChild(label);
shortcut.appendChild(div);
});
}
function requireAccess(nextAction) {
const access = getCookie('access');
if (access === '9KxH6236') {
nextAction && nextAction();
} else {
showPasswordForm(nextAction);
}
}
function showPasswordForm(onSuccess) {
shortcut.className = 'password-container';
shortcut.innerHTML = '';
const header = document.createElement('div');
header.id = 'settingsHeader';
header.style.display = 'flex';
header.style.alignItems = 'center';
header.style.justifyContent = 'center';
header.style.position = 'relative';
header.style.marginBottom = '15px';
header.style.padding = '12px';
header.style.fontSize = '16px';
header.style.color = '#000';
header.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
const backLink = document.createElement('a');
backLink.id = 'blogBackLink';
backLink.innerHTML = '<span class="backArrow"></span> 목록';
backLink.style.position = 'absolute';
backLink.style.left = '0px';
backLink.style.fontSize = '16px';
backLink.style.top = '3px';
backLink.style.color = '#007AFF';
backLink.style.textDecoration = 'none';
backLink.style.fontWeight = 'normal';
backLink.style.cursor = 'pointer';
backLink.addEventListener('click', () => renderIcons());
backLink.querySelector('.backArrow').style.cssText = `
font-size: 26px;
position: relative;
top: 1px;
margin-right: 1px;
`;
const title = document.createElement('span');
title.className = 'headerTitle';
title.textContent = '비밀번호';
title.style.fontWeight = 'bold';
header.appendChild(backLink);
header.appendChild(title);
shortcut.appendChild(header);
const input = document.createElement('input');
input.type = 'password';
input.placeholder = '비밀번호';
input.id = 'blog_secret';
input.inputMode = 'numeric';
input.pattern = '[0-9]*';
const button = document.createElement('button');
button.id = 'blog_secret_check';
button.textContent = '확인';
const msg = document.createElement('div');
msg.id = 'failMsg';
const inputRow = document.createElement('div');
inputRow.style.display = 'flex';
inputRow.style.alignItems = 'center';
inputRow.appendChild(input);
inputRow.appendChild(button);
const column = document.createElement('div');
column.style.display = 'flex';
column.style.flexDirection = 'column';
column.style.alignItems = 'center';
column.appendChild(inputRow);
shortcut.appendChild(column);
const submit = async () => {
const password = input.value;
try {
const messageRes = await fetch('/custom/launcher/api.php?failMessages=1');
const messagesJson = await messageRes.json();
const failMessages = messagesJson.failMessages || [];
const res = await fetch('/custom/launcher/api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ password })
});
const data = await res.json();
if (data.result === 'ok') {
document.cookie = 'access=9KxH6236; path=/; max-age=21600; SameSite=Strict' + (location.protocol === 'https:' ? '; Secure' : '');
if (typeof onSuccess === 'function') {
onSuccess();
} else {
location.reload();
}
} else {
column.appendChild(msg);
msg.textContent = failMessages[failIndex] || '비밀번호가 틀렸습니다.';
failIndex = (failIndex + 1) % Math.max(1, failMessages.length);
input.value = '';
input.focus();
}
} catch (_) {
}
};
button.addEventListener('click', submit);
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') submit();
});
}
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null;
}
document.addEventListener('DOMContentLoaded', async () => {
renderIcons();
const findShortcut = document.getElementById('findMyShortcut');
if (findShortcut) {
findShortcut.style.userSelect = 'none';
findShortcut.style.webkitUserSelect = 'none';
findShortcut.style.msUserSelect = 'none';
findShortcut.style.MozUserSelect = 'none';
findShortcut.style.webkitTouchCallout = 'none';
findShortcut.addEventListener('dragstart', e => e.preventDefault());
}
});
if (location.pathname.split('/').filter(Boolean).length === 1) document.title = "블로그 - 채건닷컴";