MIT 라이센스라고 명시 해서 일단 동작되는 지 체크하고 패치 해서 내가 수정해서 쓰던 코드 전문 첨부함.
서버 업데이트 되고 동작 안되서 그거만 딱 패치 함.
추후에 또 패치 되면 난 모름.
원작자이신 우리 귀인 분께서 패치하시거나 삭제 요청하시면 글 날릴 예정.
(보존해도 된다고 하셔서 일단 보존함.)
코드 옮겨서 사용할 줄 모르면 패치 기다리면 됨.
-----------------------------
2025년 05/23 일부 서버 패치에 따른 복호화 불가 문제 해결
2025년 05/28 확인함.
aHR0cHM6Ly9rb25lLmdnL3Mvc29taXNvZnQvY2t0NF9jTzNJZU1CaldRRzNQY3pDYj9wPTEmY2F0ZWdvcnk9Y1dLdGtnc2tjOU5CZjFvR0pQNDV5YiNhcnRpY2xl
링크 확인하면 오토 소미 새 버전 만드심.
개쩜 링크 꺼 쓰셈 ㅋㅋㅋ
// ==/UserScript==
'use strict';
let chkp = [,,,, atob('c29taXNvZnQ=')], Down_Option, PageLoading = [], isT = [,,], MenuID = [null, null], host = document.URL.split('/')[2], npw = [], pw = [atob('c29taXNvZnQ='),atob('MjAyNXNvbWlzb2Z0'),
// =============================== Settings =======================================
// 추가하길 원하는 비밀번호 따옴표 - 쉼표로 구분해서 바로 아래줄에 넣으면 됨 ex) '1234', '2024국룰', '!국룰!'
];
PageLoading[0] = 1000; // 페이지 로딩 시간 조절 (1000당 1초)
Down_Option = 0; // Kiosk 다운로드 옵션 0:Basic, 1:Fast, 2:일괄 다운로드
// ======================================================================================
// Helper function to get content element from Kone.gg Shadow DOM
function getKoneGGContentElement() {
if (host !== 'kone.gg') return null;
console.log('[AutoSomi] getKoneGGContentElement: Attempting to find content element...');
const proseContainer = document.querySelector('div.prose-container'); // This is the shadow host.
if (!proseContainer) {
console.log('[AutoSomi] getKoneGGContentElement: div.prose-container (shadow host) not found.');
return null;
}
console.log('[AutoSomi] getKoneGGContentElement: div.prose-container (shadow host) found:', proseContainer);
// For Declarative Shadow DOM, the shadow root is attached to the host element (proseContainer).
if (!proseContainer.shadowRoot) {
console.log('[AutoSomi] getKoneGGContentElement: proseContainer.shadowRoot not found. DSD might not be attached, supported, or the structure is different.');
const templateForDebug = proseContainer.querySelector('template[shadowrootmode="open"]');
if (templateForDebug) {
console.log('[AutoSomi] getKoneGGContentElement: Debug - template[shadowrootmode="open"] element exists. ShadowRoot should be on proseContainer.');
} else {
console.log('[AutoSomi] getKoneGGContentElement: Debug - template[shadowrootmode="open"] element also not found.');
}
return null;
}
console.log('[AutoSomi] getKoneGGContentElement: Accessed proseContainer.shadowRoot successfully.');
let contentDiv = proseContainer.shadowRoot.querySelector('div.dark'); // Try new selector first
if (contentDiv) {
console.log('[AutoSomi] getKoneGGContentElement: Found content with new selector "div.dark":', contentDiv);
return contentDiv;
}
console.log('[AutoSomi] getKoneGGContentElement: New selector "div.dark" not found, trying old selector "div.content".');
contentDiv = proseContainer.shadowRoot.querySelector('div.content'); // Fallback to old selector
if (!contentDiv) {
console.log('[AutoSomi] getKoneGGContentElement: Neither "div.dark" nor "div.content" found within proseContainer.shadowRoot.');
return null;
}
console.log('[AutoSomi] getKoneGGContentElement: Found content with old selector "div.content":', contentDiv);
return contentDiv;
}
// 미리보기 방지 창 숨기기 또는 자동 클릭 함수
function handleBlockingModals(currentHost) {
console.log(`[AutoSomi] ${currentHost} 에서 방해 요소 처리 시도... (handleBlockingModals 시작)`);
if (currentHost === 'kone.gg') {
const nsfwOverlayContainer = document.querySelector('div.relative.min-h-60 > div.absolute.w-full.h-full.backdrop-blur-2xl');
if (nsfwOverlayContainer && nsfwOverlayContainer.offsetParent !== null) { // 화면에 보일 때만
const viewContentButton = nsfwOverlayContainer.querySelector('div.flex.gap-4 button:nth-child(2)');
if (viewContentButton && viewContentButton.textContent?.includes('콘텐츠 보기')) {
console.log('[AutoSomi] Kone.gg: NSFW "콘텐츠 보기" 버튼 클릭 시도.');
viewContentButton.click();
return new Promise(resolve => setTimeout(resolve, 500));
} else {
const modalSelectorsKone = [
'.age-verification-popup',
'.content-overlay.block',
];
modalSelectorsKone.forEach(selector => hideElement(selector));
}
}
} else if (currentHost === 'arca.live') {
const modalSelectorsArca = [
{ selector: '.adult-confirm-modal', action: 'hide' },
{ selector: '.fc-dialog', action: 'hide' },
{ selector: '#preview-block-layer', action: 'hide' },
{ selector: 'div[class*="adult-channel-confirm"]', action: 'hide' }, // 먼저 숨기고
{ selector: 'div.modal[data-id="confirmAdult"] div.modal-footer button.btn-primary', action: 'click'}, // 성인 확인 모달의 확인 버튼
{ selector: 'button.btn-primary.btn.text-light[data-bs-dismiss="modal"]', action: 'click' } // 일반적인 "네, 확인했습니다" 버튼
];
modalSelectorsArca.forEach(item => {
const elements = document.querySelectorAll(item.selector);
elements.forEach(element => {
if (element && element.offsetParent !== null) {
if (item.action === 'click') {
console.log(`[AutoSomi] Arca.live: "${item.selector}" 버튼 클릭 시도.`);
element.click();
} else {
hideElement(item.selector);
}
}
});
});
}
function hideElement(selector) {
try {
const elements = document.querySelectorAll(selector);
if (elements.length > 0) {
elements.forEach(el => {
if (el.offsetParent !== null) {
el.style.setProperty('display', 'none', 'important');
console.log(`[AutoSomi] 요소 숨김 처리:`, el);
}
});
}
} catch (e) {
console.error(`[AutoSomi] 선택자 "${selector}" 처리 중 오류:`, e);
}
}
console.log(`[AutoSomi] ${currentHost} 에서 방해 요소 처리 완료 (handleBlockingModals 종료)`);
return Promise.resolve();
}
async function toggleDown(){
isT[0]=!isT[0];
if(!isT[0] && isT[1]){
isT[1]=false;
await GM.setValue('isT[1]', isT[1]);
}
await GM.setValue('isT[0]', isT[0]);
updateDown();
updateTab();
}
async function toggleTab(){
isT[1]=!isT[1];
if(!isT[0] && isT[1]){
isT[0]=true;
await GM.setValue('isT[0]', isT[0]);
}
await GM.setValue('isT[1]', isT[1]);
updateDown();
updateTab();
}
function updateDown(){
if(MenuID[0] !==null)GM_unregisterMenuCommand(MenuID[0]);
MenuID[0]=GM_registerMenuCommand(`자동 다운로드 ${isT[0] ? 'ON' : 'OFF'}`, toggleDown, { autoClose: false, title: `자동 다운로드 ${isT[0] ? '켜짐' : '꺼짐'}`});
}
function updateTab(){
if(MenuID[1] !==null)GM_unregisterMenuCommand(MenuID[1]);
MenuID[1]=GM_registerMenuCommand(`자동 탭 닫기 ${isT[1] ? 'ON' : 'OFF'}`, toggleTab, { autoClose: false, title: `자동 탭 닫기 ${isT[1] ? '켜짐' : '꺼짐'}`});
}
function doDec() {
console.log('[AutoSomi] doDec: 함수 시작');
if (chkp[3] !== chkp[4]) {
console.log('[AutoSomi] doDec: 인증 실패로 중단.');
return;
}
let targets = [];
if (host === 'arca.live') {
console.log('[AutoSomi] doDec: Arca.live 타겟 설정 시도.');
targets = [
document.querySelector('body div.article-body > div.fr-view.article-content'),
...document.querySelectorAll('div.article-comment#comment div.comment-content, div.article-comment div.comment-content')
].filter(el => el !== null);
console.log('[AutoSomi] doDec: Arca.live 타겟 수:', targets.length);
} else if (host === 'kone.gg') {
console.log('[AutoSomi] doDec: Kone.gg 타겟 설정 시도.');
const koneContentElement = getKoneGGContentElement();
if (koneContentElement) {
console.log('[AutoSomi] doDec: Kone.gg 본문 Shadow DOM contentDiv 찾음.');
} else {
console.log('[AutoSomi] doDec: Kone.gg 본문 Shadow DOM contentDiv 찾지 못함.');
}
const comments = document.querySelectorAll('p.text-sm.max-w-xl.whitespace-pre-wrap');
const listItems = document.querySelectorAll('ol.list-decimal li p');
targets = [koneContentElement, ...comments, ...listItems].filter(el => el !== null);
console.log('[AutoSomi] doDec: Kone.gg 타겟 수 (본문 + 댓글 등):', targets.length, '본문 유효성:', !!koneContentElement);
}
if (targets.length === 0 || (targets.length === 1 && !targets[0])) {
console.log('[AutoSomi] doDec: 처리할 유효한 타겟 없음. 함수 종료.');
return;
}
function dec(target, reg) {
try {
if (!target || !target.innerHTML) {
console.log('[AutoSomi] dec 내부: target이 없거나 innerHTML이 없음.', target);
return;
}
const original = target.innerHTML;
const matches = [...original.matchAll(reg)];
if (matches.length === 0) return;
console.log(`[AutoSomi] dec 내부: ${matches.length}개의 잠재적 Base64 문자열 발견 (${reg.source})`, target);
let result = original;
let decodedCount = 0;
for (const match of matches) {
let encoded = match[0];
let decoded = encoded;
try {
while (decoded && typeof decoded === 'string' && !decoded.startsWith('http') && decoded.length > 10 && decoded.length < 1000) {
let tempDecoded = atob(decoded);
if (tempDecoded === decoded) break; // No change after decoding, likely not base64 or already decoded
decoded = tempDecoded;
}
if (decoded && typeof decoded === 'string' && decoded.startsWith('http')) {
const linkHTML = `<a href="${decoded}" target="_blank" rel="noreferrer" style="color:#007bff; text-decoration:underline; word-break:break-all;">${decoded}</a>`;
result = result.replace(encoded, linkHTML);
decodedCount++;
}
} catch (e) {
// console.warn('[AutoSomi] dec 내부: 개별 복호화 실패:', encoded, e.message);
}
}
if (target.innerHTML !== result) {
target.innerHTML = result;
console.log(`[AutoSomi] dec 내부: ${decodedCount}개의 링크로 변환 완료.`, target);
}
} catch (e) {
console.error('[AutoSomi] dec 함수 에러:', e, target);
}
}
for (const target of targets) {
if (!target) continue;
// Ensure all existing links have noreferrer
const links = target.querySelectorAll('a');
links.forEach(a => {
a.setAttribute('rel', 'noreferrer');
});
dec(target, /aHR0c[0-9A-Za-z+/=]{8,}/g); // Standard Base64 starting with http
dec(target, /YUhSMG[0-9A-Za-z+/=]{8,}/g); // Standard Base64 starting with aHR0 (http)
dec(target, /WVVoU[0-9A-Za-z+/=]{8,}/g); // Double Base64
dec(target, /V1ZWb[0-9A-Za-z+/=]{8,}/g); // Triple Base64
}
console.log('[AutoSomi] doDec: 함수 종료. doDlsiteContextAware 호출 예정.');
setTimeout(doDlsiteContextAware, 100); // Call DLsite link generation after decoding
}
function doDlsiteContextAware() {
console.log('[AutoSomi] doDlsiteContextAware: 함수 시작');
let atc; // Article content element
if (host === 'arca.live') {
atc = document.querySelector('.article-body .article-content');
console.log('[AutoSomi] doDlsiteContextAware: Arca.live 본문 선택자 결과:', atc);
} else if (host === 'kone.gg') {
atc = getKoneGGContentElement();
console.log('[AutoSomi] doDlsiteContextAware: Kone.gg 본문 (getKoneGGContentElement) 결과:', atc);
}
if (!atc) {
console.log('[AutoSomi] doDlsiteContextAware: 본문(atc) 찾을 수 없음. 함수 종료.');
return;
}
// console.log('[AutoSomi] doDlsiteContextAware: atc.innerHTML (일부):', atc.innerHTML.substring(0, 200) + "...");
let titleEl;
if (host === 'arca.live') {
titleEl = document.querySelector('.title-row .title') || document.querySelector('.board-title .title');
} else if (host === 'kone.gg') {
titleEl = [...document.querySelectorAll('h1')].find(el =>
el.textContent?.match(/RJ[0-9]{6,10}/i) // Find h1 that looks like it contains an RJ code
);
}
console.log('[AutoSomi] doDlsiteContextAware: titleEl 결과:', titleEl);
const keywordPattern = /(꺼|거|퍼|RJ|rj|Rj|rJ|VJ|vj|Vj|vJ|DL|dl|Dl|dL)[\s:()\[\]#-]*([0-9]{6,10})/g;
const fullRJPattern = /\b(RJ|rj|Rj|rJ|VJ|vj|Vj|vJ|퍼|꺼|거|DL|dl|Dl|dL)([0-9]{6,10})\b/g; // More precise, word boundary
let titleText = '';
if (titleEl?.childNodes) { // Check if titleEl and its childNodes exist
titleText = Array.from(titleEl.childNodes)
.map(n => n.textContent?.trim() || '') // Ensure textContent exists and trim
.join(' ');
}
const bodyText = atc.textContent || ''; // Ensure textContent exists
const fullText = titleText + '\n' + bodyText;
// 1. Matches from full text (title + body)
const matchList1 = [...fullText.matchAll(keywordPattern)].map(m => ({ prefix: m[1], code: m[2] }));
const matchList2 = [...fullText.matchAll(fullRJPattern)].map(m => ({ prefix: m[1], code: m[2] }));
// 2. Matches from existing links in the body
const linkCodes = Array.from(atc.querySelectorAll('a[href]'))
.map(a => {
const match = a.href.match(/(RJ|VJ)([0-9]{6,10})/i); // Match RJ or VJ codes in href
return match ? { prefix: match[1].toUpperCase(), code: match[2] } : null;
})
.filter(Boolean); // Remove nulls
// 3. Matches from split text nodes (e.g., "RJ" <span>123456</span>)
const splitNodeMatches = [];
const allNodes = [...atc.querySelectorAll('*:not(script):not(style):not(a)')]; // Get all elements except script, style, a
const validPrefixes = ['꺼','거','퍼','RJ','rj','Rj','rJ','VJ','vj','Vj','vJ','DL','dl','Dl','dL'];
for (const node of allNodes) {
const children = [...node.childNodes];
for (let i = 0; i < children.length - 1; i++) {
const a = children[i], b = children[i + 1];
if (a.nodeType !== Node.TEXT_NODE || b.nodeType !== Node.TEXT_NODE) continue;
const aText = (a.textContent || '').replace(/\s+/g, ''); // Text of first node, no spaces
const bText = (b.textContent || '').trim(); // Text of second node, trimmed
if (!/^\d{6,10}$/.test(bText)) continue; // Second node must be a 6-10 digit number
for (const prefix of validPrefixes) {
if (aText.endsWith(prefix)) {
splitNodeMatches.push({ prefix, code: bText });
break;
}
}
}
}
// 4. Extra check for codes at the very start of the title (e.g., "[123456] Title")
const extraFromTitle = [];
const matchInTitleStart = titleText.match(/^[\[]?([0-9]{6,10})[\]]?/); // Optional brackets
if (matchInTitleStart) extraFromTitle.push({ prefix: 'RJ', code: matchInTitleStart[1] }); // Default to RJ
// Combine all found codes
const allCodes = [
...matchList1,
...matchList2,
...linkCodes,
...splitNodeMatches,
...extraFromTitle
];
// Deduplicate codes
const seen = new Set();
const uniqueCodes = allCodes.filter(({ code }) => {
if (!code || seen.has(code)) return false;
seen.add(code);
return true;
});
console.log('[AutoSomi] doDlsiteContextAware: 찾은 DL 코드 수 (유니크):', uniqueCodes.length, '전체:', allCodes.length);
// If exactly one unique code is found, append a prominent link at the bottom
if (uniqueCodes.length === 1) {
const { code, prefix } = uniqueCodes[0];
if (!code) { // Should not happen due to filter, but as a safeguard
console.log('[AutoSomi] doDlsiteContextAware: 유니크 코드 1개지만 코드 없음. 종료.');
return;
}
// Map various prefixes to standard RJ or VJ
const mappedPrefix = ['vj', 'vJ', 'Vj', 'VJ', '퍼'].includes(prefix.toLowerCase()) ? 'VJ'
: ['rj', 'rJ', 'Rj', 'RJ', '꺼', '거', 'dl', 'dL', 'Dl', 'DL'].includes(prefix.toLowerCase()) ? 'RJ'
: prefix.toUpperCase(); // Fallback, though should be covered
const fullCode = `${mappedPrefix}${code}`;
const link = `https://www.dlsite.com/maniax/work/=/product_id/${fullCode}.html`;
const existing = atc.querySelector('.dlsite-link-appended');
if (existing) existing.remove(); // Remove if already appended
const wrapper = document.createElement('div');
wrapper.className = 'dlsite-link-appended';
wrapper.style.marginTop = '15px';
wrapper.style.padding = '10px 0';
wrapper.style.borderTop = '1px dashed #aaa';
const a = document.createElement('a');
a.href = link;
a.target = '_blank';
a.rel = 'noreferrer';
a.textContent = `[DLsite] ${fullCode}`;
a.style.display = 'block';
a.style.marginBottom = '5px';
a.style.color = '#1e90ff'; // A distinct blue color
wrapper.appendChild(a);
atc.appendChild(wrapper);
console.log('[AutoSomi] doDlsiteContextAware: 유니크 코드 1개 발견, 링크 추가 완료:', fullCode);
return; // Done for single unique code
}
// If multiple codes or no single prominent code, try to convert existing text instances into links
if (uniqueCodes.length > 1) console.log('[AutoSomi] doDlsiteContextAware: 여러 DL 코드 발견, 텍스트 노드 순회하며 링크 변환 시도.');
const combinedPattern = new RegExp(`${keywordPattern.source}|${fullRJPattern.source}`, 'gi'); // Combine patterns for text node search
let linkConversionCount = 0;
const walk = (node) => {
if (node.nodeType === Node.TEXT_NODE) {
const text = node.textContent;
if (!text) return;
let lastIndex = 0;
let match;
const frag = document.createDocumentFragment();
while ((match = combinedPattern.exec(text)) !== null) {
const before = text.slice(lastIndex, match.index);
const number = match[2] || match[4]; // RJ code number from either pattern
const prefix = (match[1] || match[3] || '').toLowerCase(); // Prefix from either pattern
const mappedPrefix = ['vj', 'vJ', 'Vj', 'VJ', '퍼'].includes(prefix) ? 'VJ'
: ['rj', 'rJ', 'Rj', 'RJ', '꺼', '거', 'dl', 'dL', 'Dl', 'DL'].includes(prefix) ? 'RJ'
: prefix.toUpperCase();
const fullCode = `${mappedPrefix}${number}`;
const a = document.createElement('a');
a.href = `https://www.dlsite.com/maniax/work/=/product_id/${fullCode}.html`;
a.textContent = match[0]; // Use the original matched text for the link
a.target = '_blank';
a.rel = 'noreferrer';
a.style.color = '#1e90ff';
if (before) frag.appendChild(document.createTextNode(before));
frag.appendChild(a);
linkConversionCount++;
lastIndex = match.index + match[0].length;
}
if (lastIndex < text.length) {
frag.appendChild(document.createTextNode(text.slice(lastIndex)));
}
if (frag.childNodes.length > 0 && node.parentNode && frag.textContent !== node.textContent) { // Only replace if changes were made
node.parentNode.replaceChild(frag, node);
}
} else if (
node.nodeType === Node.ELEMENT_NODE &&
node.tagName && // Make sure tagName exists
!['A', 'SCRIPT', 'STYLE'].includes(node.tagName.toUpperCase()) // Don't traverse into existing links or script/style tags
) {
for (const child of [...node.childNodes]) { // Iterate over a copy of childNodes
walk(child);
}
}
};
walk(atc); // Start walking from the article content element
if(linkConversionCount > 0) console.log(`[AutoSomi] doDlsiteContextAware: ${linkConversionCount}개의 텍스트를 DLsite 링크로 변환 완료.`);
console.log('[AutoSomi] doDlsiteContextAware: 함수 종료.');
}
async function chkPW(){
chkp[3]=await GM.getValue('chkp[3]');
isT[0]=await GM.getValue('isT[0]', true); // Default auto download to true
isT[1]=await GM.getValue('isT[1]', false); // Default auto tab close to false
updateDown();
updateTab();
if(host=='arca.live' || host=='kone.gg'){ // Only prompt for auth on these sites
if(chkp[3] !=chkp[4]){ // If stored auth value doesn't match the expected one
console.log('[AutoSomi] chkPW: 인증 필요.');
const chk=prompt(decodeURI(atob('JUVBJUI1JUFEJUVCJUEzJUIwJUVEJTk5JTk1JUVDJTlEJUI4'))); // "인증번호를 입력하세요"
if(chk?.toLowerCase()==chkp[4]) { // chkp[4] is atob('c29taXNvZnQ=') -> "somisoft"
await GM.setValue('chkp[3]', chkp[4]);
console.log('[AutoSomi] chkPW: 인증 성공.');
} else {
GM.setValue('chkp[3]', false); // Store false on failure
console.log('[AutoSomi] chkPW: 인증 실패.');
alert(decodeURI(atob('JUVBJUI1JUFEJUVCJUEzJUIwJUVDJTlEJUI0JTIwJUVDJTlEJUJDJUVDJUI5JTk4JUVEJTk1JTk4JUVDJUE3JTgwJTIwJUVDJTk1JThBJUVDJThBJUI1JUVCJThCJTg4JUVCJThCJUE0'))); // "인증번호가 정확하지 않거나 입력되지 않았습니다"
}
} else {
console.log('[AutoSomi] chkPW: 이미 인증됨.');
}
}
}
async function inputPW() {
console.log('[AutoSomi] inputPW: 함수 시작');
let inputElem = document.querySelector(chkp[0]), btnElem = document.querySelector(chkp[1]);
if (!inputElem ) { // If password input field is not found
console.log(`[AutoSomi] inputPW: 비밀번호 입력 필드(${chkp[0]}) 없음.`);
if (isT[0] === true && !document.querySelector('.files-list, #download-section')) { // If auto download is on and no files list (meaning password might have been bypassed or not needed)
console.log('[AutoSomi] inputPW: 자동 다운로드 시도 (DBtn 호출).');
await setTimeout(DBtn, PageLoading[1] || 1000); // PageLoading[1] is site-specific delay
}
return;
}
console.log('[AutoSomi] inputPW: 비밀번호 입력 필드 및 버튼 찾음:', inputElem, btnElem);
const combinedPw = [...new Set([...pw, ...npw])]; // Combine default passwords and found passwords, ensure uniqueness
console.log('[AutoSomi] inputPW: 시도할 비밀번호 목록:', combinedPw);
if (chkp[3] == chkp[4]) { // Only try passwords if authenticated
try {
for (let i = 0; i < combinedPw.length; i++) {
console.log(`[AutoSomi] inputPW: ${i+1}번째 비밀번호 시도: ${combinedPw[i]}`);
if (!combinedPw[i]) continue; // Skip empty passwords
if (!inputElem) { // Check if input element disappeared (e.g., due to successful login)
console.log('[AutoSomi] inputPW: 루프 중 비밀번호 입력 필드 사라짐.');
break;
}
inputElem.value = combinedPw[i];
if (host == 'kio.ac') { // Specific handling for kio.ac (needs input/change events)
inputElem.dispatchEvent(new Event('input', { bubbles: true }));
inputElem.dispatchEvent(new Event('change', { bubbles: true }));
await new Promise(res => setTimeout(res, 50)); // Short delay
if(btnElem) btnElem.click();
} else { // General case
if(btnElem) btnElem.click(); // Click submit button if exists
else { // If no button, try dispatching Enter key event
const enterEvent = new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true });
inputElem.dispatchEvent(enterEvent);
}
}
await new Promise(res => setTimeout(res, 800)); // Wait for page to react
// Check for success or error indicators
const successIndicator = document.querySelector('.files-list, #download-section, .download-link, .btn-download, .main-button-download');
const errorIndicator = document.querySelector('.text-error, .text-red-500, .error-message, .incorrect-password, [class*="error"], [id*="error"]');
if (successIndicator && successIndicator.offsetParent !== null) { // If success indicator is visible
console.log(`[AutoSomi] inputPW: 비밀번호 "${combinedPw[i]}" 성공!`);
break; // Password worked, exit loop
} else if (errorIndicator && errorIndicator.offsetParent !== null) { // If error indicator is visible
console.log(`[AutoSomi] inputPW: 비밀번호 "${combinedPw[i]}" 실패 또는 오류 표시됨.`);
if (inputElem) inputElem.value = ''; // Clear input for next try
} else {
console.log(`[AutoSomi] inputPW: 비밀번호 "${combinedPw[i]}" 입력 후 성공/실패 표시기 없음.`);
// Potentially a page change or intermediate state, might need to re-evaluate inputElem
inputElem = document.querySelector(chkp[0]); // Re-query input element
btnElem = document.querySelector(chkp[1]); // Re-query button element
}
}
if (isT[0] == true) { // If auto download is on
console.log('[AutoSomi] inputPW: 자동 다운로드 시도 (DBtn 호출).');
await setTimeout(DBtn, PageLoading[1] || 1000);
}
} catch (e) {
console.error("[AutoSomi] inputPW: 암호 입력 중 오류:", e);
if (isT[0] == true) { // Still try to download if error occurred
console.log('[AutoSomi] inputPW: 오류 후 자동 다운로드 시도 (DBtn 호출).');
await setTimeout(DBtn, PageLoading[1] || 1000);
}
}
} else {
console.log('[AutoSomi] inputPW: 인증되지 않아 비밀번호 입력 건너뜀.');
}
console.log('[AutoSomi] inputPW: 함수 종료.');
}
async function kioskdone(){
console.log('[AutoSomi] kioskdone: 함수 시작');
try {
await new Promise(res=> setTimeout(res, 3000)); // Initial delay
for(var i=0, jj=0; jj!=1 && i < 60; i++){ // Try for up to 60 seconds
await new Promise(res=> setTimeout(res, 1000)); // Check every second
const doneElement = document.querySelector('.flex.flex-row.text-xs.justify-between div:nth-child(2)'); // Kiosk specific "done" element
if(doneElement?.innerText.toLowerCase()=='done'){
console.log('[AutoSomi] kioskdone: "done" 상태 확인, 탭 닫기 시도.');
await setTimeout(()=> { window.open('', '_self').close()}, 2000); // Close tab after 2 seconds
jj++;
break; // Exit loop
} else {
// console.log(`[AutoSomi] kioskdone: "done" 상태 아님 (시도 ${i+1}/60)`);
}
}
if (jj === 0) console.log('[AutoSomi] kioskdone: "done" 상태 확인 시간 초과.');
} catch(e){
console.error("[AutoSomi] kioskdone 오류:", e);
if(isT[1]==true && isT[2]==true) setTimeout(()=> { window.open('', '_self').close()}, 1000); // Close tab if auto-close is on
}
console.log('[AutoSomi] kioskdone: 함수 종료.');
}
async function DBtn(){ // Download Button clicker
console.log('[AutoSomi] DBtn: 함수 시작');
if(isT[0] !== true) { // If auto download is off
console.log('[AutoSomi] DBtn: 자동 다운로드 꺼져있음.');
return;
}
if(host=='mega.nz') { // Mega.nz specific download buttons
try {
console.log('[AutoSomi] DBtn: Mega.nz 다운로드 버튼 클릭 시도.');
const resumeButton = document.querySelector('.mega-button.positive.resume.js-resume-download');
if (resumeButton) {
console.log('[AutoSomi] DBtn: Mega 이어받기 버튼 클릭.');
resumeButton.click();
await new Promise(res => setTimeout(res, 500));
}
const standardDownloadButton = document.querySelector('.mega-button.positive.js-default-download.js-standard-download');
if (standardDownloadButton) {
console.log('[AutoSomi] DBtn: Mega 표준 다운로드 버튼 클릭.');
standardDownloadButton.click();
await new Promise(res => setTimeout(res, 2000)); // Longer delay for Mega
}
const continueDownloadButton = document.querySelector('.mega-button.large.positive.download.continue-download'); // Another possible button
if (continueDownloadButton) {
console.log('[AutoSomi] DBtn: Mega 계속 다운로드 버튼 클릭.');
continueDownloadButton.click();
}
} catch(e) {
console.error("[AutoSomi] DBtn: Mega 다운로드 버튼 클릭 오류:", e);
}
} else { // General download button clicker
try {
console.log(`[AutoSomi] DBtn: 일반 다운로드 버튼(${chkp[2]}) 클릭 시도.`);
let btns = document.querySelectorAll(chkp[2]); // chkp[2] is the download button selector for the current site
let delayMultiplier = (PageLoading[0] || 1000) + 600; // Base delay + 0.6s
if(btns.length > 1){ // If multiple download buttons (e.g., batch download)
console.log(`[AutoSomi] DBtn: ${btns.length}개의 버튼(일괄) 클릭 시도.`);
btns.forEach((btn, index) => {
setTimeout(() => {
console.log(`[AutoSomi] DBtn: 버튼 ${index + 1} 클릭.`);
btn.click();
}, index * delayMultiplier); // Stagger clicks
});
} else if(btns.length === 1){ // Single download button
console.log('[AutoSomi] DBtn: 단일 버튼 클릭.');
btns[0].click();
} else {
console.log('[AutoSomi] DBtn: 다운로드 버튼 찾지 못함.');
}
} catch(e){ console.error("[AutoSomi] DBtn: 일반 다운로드 버튼 클릭 오류:", e); }
}
if(isT[1]==true && isT[2]==true){ // If auto tab close is ON and site allows tab close (isT[2])
console.log('[AutoSomi] DBtn: 자동 탭 닫기 활성화됨.');
let btnsForDelay = document.querySelectorAll(chkp[2]);
let totalDelay = (btnsForDelay.length > 1) ? (btnsForDelay.length * ((PageLoading[0] || 1000) + 600) + 700) : 1300; // Calculate delay based on number of buttons
if(host=='kiosk.ac'){
console.log('[AutoSomi] DBtn: Kiosk.ac 탭 닫기 예약 (kioskdone 호출).');
setTimeout(()=> { kioskdone(); }, totalDelay); // Kiosk has specific done check
} else if(host=='kio.ac'){
console.log('[AutoSomi] DBtn: Kio.ac 탭 닫기 예약.');
setTimeout(()=> { window.open('', '_self').close()}, totalDelay+1000); // Slightly longer delay for kio.ac
} else {
console.log('[AutoSomi] DBtn: 일반 탭 닫기 예약.');
setTimeout(()=> { window.open('', '_self').close()}, totalDelay);
}
}
console.log('[AutoSomi] DBtn: 함수 종료.');
}
function FindPW(){ // Finds passwords in page content
console.log('[AutoSomi] FindPW: 함수 시작');
let atc; // Article content element
if (host === 'arca.live') {
atc = document.querySelector('body div.article-body > div.fr-view.article-content');
console.log('[AutoSomi] FindPW: Arca.live 본문 선택자 결과:', atc);
} else if (host === 'kone.gg') {
atc = getKoneGGContentElement();
console.log('[AutoSomi] FindPW: Kone.gg 본문 (getKoneGGContentElement) 결과:', atc);
}
if (!atc || !atc.innerHTML) { // If no content element or it's empty
console.log('[AutoSomi] FindPW: 본문(atc) 없거나 innerHTML 없음. 함수 종료.');
return;
}
// console.log('[AutoSomi] FindPW: atc.innerHTML (일부):', atc.innerHTML.substring(0, 200) + "...");
let tempContent = atc.innerHTML;
tempContent=tempContent.replace(/ /g, ' ').replace(/( ){2,}/g, ' '); // Normalize spaces (first replace seems redundant)
tempContent=tempContent.replace(/국룰/g, 'ㄱㄹ'); // Replace "국룰" with "ㄱㄹ" for matching
let regexx=/(대문자)/; // Regex for "대문자" (uppercase)
if(regexx.test(tempContent)) { // If "대문자" keyword is found
const smpeopleUpper = atob('U01QRU9QTEU='); // "SMPEOPLE"
if (npw.indexOf(smpeopleUpper) === -1) { // Add if not already present
npw.push(smpeopleUpper);
console.log('[AutoSomi] FindPW: "대문자" 키워드로 "SMPEOPLE" 추가.');
}
}
function dec(reg){ // Inner function to process matches from regex
let foundPasswordsInDec = 0;
try {
let currentLoopContent = tempContent; // Use a mutable copy of the content for this loop
const processedTexts = new Set(); // To avoid processing the same matched text multiple times if regex is not advancing properly
while(true){ // Loop through all matches
const matchResult = reg.exec(currentLoopContent);
if (!matchResult) break; // No more matches
let matchedText = matchResult[0];
if (processedTexts.has(matchedText)) {
// If this exact text was already processed, replace it to ensure exec moves on
currentLoopContent = currentLoopContent.replace(matchedText, `__SKIPPED_${Math.random().toString(36).substring(2, 10)}__`);
continue;
}
let DECed = matchedText.replace(/(ㄱㄹ)/g, '국룰'); // Restore "ㄱㄹ" to "국룰"
// Remove common surrounding words and characters, then take the first part before certain Korean syllables
let DECedd = DECed.replace(/\s|[+]|(은|는|이|가)|(전부)|(대문자로)|(대문자)|(비밀번호)|(패스워드)|(비번)|(ㅂㅂ)|(암호)|(ㅇㅎ)|(키오스크맘)|(키오스크)|(입니다)|(이고)|(이며)|(임다)|(같다)|(처럼)|(틀리다)|(입니다요)/g, '');
DECedd = DECedd.split(/[(입)(임)(이)(이)(이)(입)(임)(같)(처)(틀)]/g)[0]; // Split by common suffixes/particles
DECedd = DECedd.replace(/[<>]/g, ''); // Remove angle brackets
let dat; // Date string
if (host === 'arca.live') { // Get date from arca.live article info
const dateEl = document.querySelector('.article-info .date .body');
if (dateEl && typeof dateEl.innerText === 'string' && dateEl.innerText.trim() !== '') {
dat = dateEl.innerText.split(' ')[0]; // YYYY-MM-DD
} else {
dat = undefined;
}
}
let regexa=/(오늘)|(날짜)|(날자)/g; // Regex for "today" or "date"
if(dat && regexa.test(DECedd)){ // If date is available and keyword "today/date" is in the password candidate
DECedd=DECedd.replace(regexa, ''); // Remove "today/date" keyword
const dateParts = dat.split(/\-/g); // Split YYYY-MM-DD
if (dateParts.length === 3) {
const year = dateParts[0].slice(2); // YY
const month = dateParts[1]; // MM
const day = dateParts[2]; // DD
let dateFormats=[month + day, month + '-' + day, year + month + day, dat.replace(/-/g,'')]; // MMDD, MM-DD, YYMMDD, YYYYMMDD
for(let k=0;k<dateFormats.length;k++){
const finalPw = DECedd.replace(/국룰/g,chkp[4]) + dateFormats[k]; // Append date to password candidate (chkp[4] is "somisoft")
if(finalPw.length > 2 && npw.indexOf(finalPw)=='-1') { // If valid length and not duplicate
npw.push(finalPw);
foundPasswordsInDec++;
}
}
}
} else { // If not a date-related password
const finalPw = DECedd.replace(/국룰/g,chkp[4]); // Replace "국룰" with "somisoft"
if(finalPw && finalPw.length > 2 && npw.indexOf(finalPw)=='-1') { // Check length and uniqueness
npw.push(finalPw);
foundPasswordsInDec++;
}
}
processedTexts.add(matchedText); // Mark this text as processed
// To ensure the loop progresses, replace the processed part in currentLoopContent
// This is crucial if the regex `reg` might re-match the same substring.
currentLoopContent = currentLoopContent.substring(0, matchResult.index) +
`__PROCESSED_${Math.random().toString(36).substring(2, 15)}__` +
currentLoopContent.substring(matchResult.index + matchedText.length);
}
if (foundPasswordsInDec > 0) console.log(`[AutoSomi] FindPW dec: ${foundPasswordsInDec}개의 비밀번호 후보 추가됨.`);
GM.setValue('npw', npw); // Save found passwords
} catch(e){ console.error("[AutoSomi] FindPW dec 함수 오류:", e); }
}
// Regex to find potential passwords: text possibly wrapped in <p>, containing "ㄱㄹ" (국룰), surrounded by other characters
dec(/[(<p>\s*)*]{0,}[ㄱ-ㅣ가-힣0-9A-Za-z\s~`!^\_+@\#$%&=]{0,}(ㄱㄹ){1,1}[ㄱ-ㅣ가-힣0-9A-Za-z\s~`!^\_+@\#$%&=]{0,}[(\s*</p>)*]{0,}/);
console.log('[AutoSomi] FindPW: 함수 종료. aaa 호출 예정.');
setTimeout(aaa, 100); // Load any previously saved passwords
}
function waitForKoneContent() { // Specifically for kone.gg to wait for dynamic content
console.log('[AutoSomi] waitForKoneContent: 함수 시작');
let attempts = 0;
const maxAttempts = 20; // Try for about 6 seconds (20 * 300ms)
const checkInterval = setInterval(async () => {
attempts++;
// console.log(`[AutoSomi] waitForKoneContent: 시도 ${attempts}/${maxAttempts}`);
await handleBlockingModals(host); // Handle any popups first
const atc = getKoneGGContentElement(); // Get content from Shadow DOM
const titleEl = [...document.querySelectorAll('h1.flex, h1.text-xl')].find(el => // Check for title with RJ/VJ code
/RJ[0-9]{6,10}|VJ[0-9]{6,10}/i.test(el.textContent || '')
);
const hasBase64 = atc && /aHR0c|YUhSMG|WVVoU|V1ZWb/.test(atc.textContent || ''); // Check if content has Base64 strings
// Conditions to proceed: content element exists, AND (title has RJ/VJ OR content has Base64), AND content has some length
if (atc && (titleEl || hasBase64) && (atc.textContent || '').length > 10) {
console.log('[AutoSomi] waitForKoneContent: 콘텐츠 확인됨. Interval 중지 및 처리 시작.');
clearInterval(checkInterval);
FindPW(); // Find passwords
setTimeout(() => { // Then decode and add DLsite links
doDec();
doDlsiteContextAware();
}, 300);
} else if (attempts >= maxAttempts) {
console.log('[AutoSomi] waitForKoneContent: 최대 시도 횟수 도달. Interval 중지.');
clearInterval(checkInterval);
} else {
// console.log('[AutoSomi] waitForKoneContent: 콘텐츠 아직 준비 안됨. atc:', !!atc, 'titleEl:', !!titleEl, 'hasBase64:', hasBase64, 'atc.textContent.length:', atc?.textContent?.length);
}
}, 300); // Check every 300ms
}
async function aaa() { // Loads saved passwords from GM storage
console.log('[AutoSomi] aaa: 저장된 비밀번호 로드 시도.');
const saved = await GM.getValue('npw');
if (Array.isArray(saved)) {
npw = [...new Set([...npw, ...saved])]; // Merge with existing npw and ensure uniqueness
console.log('[AutoSomi] aaa: 저장된 비밀번호 로드 완료. 현재 npw:', npw);
} else {
console.log('[AutoSomi] aaa: 저장된 비밀번호 없음.');
}
}
// === Script Start Point ===
(async () => {
console.log('[AutoSomi] 스크립트 시작. 현재 호스트:', host);
await chkPW(); // Check authentication first
await handleBlockingModals(host); // Handle any initial modals
if (host === 'arca.live') {
console.log('[AutoSomi] Arca.live 특정 로직 실행.');
FindPW(); // Find passwords
setTimeout(doDec, 200); // Decode Base64 (DLsite links are handled inside doDec via doDlsiteContextAware)
} else if (host === 'kone.gg') {
console.log('[AutoSomi] Kone.gg 특정 로직 실행 (waitForKoneContent).');
waitForKoneContent(); // Wait for dynamic content then process
}
// Site-specific configurations and password input logic
if(host=='kio.ac'){
await aaa(); // Load saved passwords
chkp[0]='.overflow-auto.max-w-full.max-h-full.flex-grow.p-1 input:nth-of-type(1)'; // Password input selector
chkp[1]='.flex.flex-col-reverse button:nth-of-type(1)'; // Submit button selector
chkp[2]='.p-2.align-middle .flex.align-middle button:nth-of-type(1)'; // Download button selector
isT[2]=true; // Allow auto tab close
PageLoading[1]=3500; // Delay before trying to download
setTimeout(inputPW, (PageLoading[0] || 1000) + 2000); // Delay before inputting password
}
if(host=='mega.nz'){
await aaa();
chkp[0]='#password-decrypt-input';
chkp[1]='.mega-button.positive.fm-dialog-new-folder-button.decrypt-link-button';
chkp[2]='.mega-button.positive.js-default-download.js-standard-download'; // This is actually the download button, not password submit
PageLoading[1]=4000;
isT[2]=false; // Mega usually keeps the tab open for download progress
setTimeout(inputPW, (PageLoading[0] || 1000) + 1800);
}
if(host=='kiosk.ac'){
await aaa();
chkp[0]='.input.shadow-xl.flex-grow';
chkp[1]='.btn.btn-ghost.w-full.mt-2.rounded-md';
if(Down_Option==2) // Batch download option
chkp[2]='.flex.justify-between.w-full .flex.gap-2 .btn.btn-ghost'; // Batch download buttons
else // Single download options (Basic or Fast)
chkp[2]='#vexplorer-body .hover\\:bg-neutral .dropdown button, #vexplorer-body > div > div > div.flex.justify-between.w-full > div.flex.gap-2 > button:not(.btn-disabled)'; // More specific selector for individual download buttons
if(Down_Option==0) // Basic download
isT[2]=true; // Allow auto tab close
else // Fast or Batch
isT[2]=false; // Keep tab open
PageLoading[1]=3000;
setTimeout(inputPW, (PageLoading[0] || 1000));
}
if(host=='workupload.com'){
await aaa();
chkp[0]='#passwordprotected_file_password';
chkp[1]='#passwordprotected_file_submit';
chkp[2]='a.btn.btn-prio[href*="/file/"]'; // Download button
if(Down_Option==0) // Assuming Down_Option is generic, 0 might mean basic/single file
isT[2]=true;
else
isT[2]=false;
PageLoading[1]=2000;
setTimeout(inputPW, (PageLoading[0] || 1000));
}
if(host=='drive.google.com'){
await aaa();
if(document.URL.includes('/file/d/')){ // Single file page
const fileId = document.URL.split('/d/')[1].split('/')[0];
if (fileId) { // Redirect to direct download link
window.location.href=`https://drive.usercontent.google.com/download?id=${fileId}&export=download&authuser=0`;
}
}
if(document.URL.includes('/drive/folders/')){ // Folder page
setTimeout(()=> {
if(isT[0]==true) { // If auto download is on
const downloadAllButton = document.querySelector('div[aria-label="모두 다운로드"], div[data-tooltip="모두 다운로드"]');
if (downloadAllButton) {
downloadAllButton.click();
} else { // Fallback if "Download All" is not found (e.g. single item in folder view)
const firstItemDownloadButton = document.querySelector('div[role="gridcell"][data-is-shared="false"] div[aria-label*="다운로드"]'); // Try to download the first item
if(firstItemDownloadButton) {
firstItemDownloadButton.click();
}
}
}
}, (PageLoading[0] || 1000) + 1500); // Delay before clicking download all
chkp[2]='.h-De-Vb.h-De-Y'; // Selector for download button in confirmation dialog (might be outdated)
PageLoading[1]=1; // Minimal delay
isT[2]=true; // Allow tab close (though GDrive download usually happens in same tab or new small window)
setTimeout(()=> { if(isT[0]==true) setTimeout(DBtn, 3500);}, (PageLoading[0] || 1000)); // Further delay for DBtn if needed
}
}
if(host=='drive.usercontent.google.com'){ // Direct download page for Google Drive
await aaa(); // Not really needed here, but consistent
chkp[2]='form[method="POST"] button[type="submit"], input[type="submit"][value="다운로드"], button.jfk-button-action'; // Various possible download buttons
isT[2]=true; // Allow tab close after download initiated
setTimeout(()=> { if(isT[0]==true) DBtn();}, 100); // Click download button quickly
}
if(host=='gofile.io'){
await aaa();
chkp[0]='#filesErrorPasswordInput';
chkp[1]='#filesErrorPasswordButton';
chkp[2]='button.btn-download, a.btn-download, #rowFolderCenter button[data-bs-target="#filesList"] + div .btn-outline-secondary'; // Download buttons (individual or from folder view)
PageLoading[1]=3000;
isT[2]=true;
setTimeout(inputPW, (PageLoading[0] || 1000));
}
console.log('[AutoSomi] 메인 즉시 실행 함수 로직 완료.');
})();
// Kone.gg specific logic for button hooks (IIFE)
(function () {
'use strict';
if (location.host !== 'kone.gg') return;
console.log('[AutoSomi] Kone.gg 특화 로직 (버튼 훅) IIFE 시작.');
function hookDecodeTriggerOnButton() {
// console.log('[AutoSomi] hookDecodeTriggerOnButton: 실행 시도.');
// This button seems to be related to loading comments or more content
const container = document.querySelector('div.flex.items-center.justify-between.p-4');
if (!container) {
// console.log('[AutoSomi] hookDecodeTriggerOnButton: 컨테이너(div.flex.items-center.justify-between.p-4) 없음.');
return;
}
const button = container.querySelector('button[data-slot="button"]'); // Generic button slot
if (!button) {
// console.log('[AutoSomi] hookDecodeTriggerOnButton: 버튼(button[data-slot="button"]) 없음.');
return;
}
if (button.dataset._somi_hooked === '1') { // Check if already hooked
// console.log('[AutoSomi] hookDecodeTriggerOnButton: 버튼 이미 훅됨.');
return;
}
button.dataset._somi_hooked = '1'; // Mark as hooked
console.log('[AutoSomi] hookDecodeTriggerOnButton: 버튼에 클릭 리스너 추가 완료.');
button.addEventListener('click', async () => {
console.log('[AutoSomi] Kone.gg 버튼 클릭됨, 처리 시작.');
await handleBlockingModals(host); // Handle modals that might appear on click
setTimeout(() => { // Delay to allow content to load after click
console.log('[AutoSomi] Kone.gg 버튼 클릭 후 FindPW 호출.');
FindPW();
setTimeout(() => {
console.log('[AutoSomi] Kone.gg 버튼 클릭 후 doDec, doDlsiteContextAware 호출.');
doDec();
doDlsiteContextAware();
}, 300);
}, 500);
});
}
hookDecodeTriggerOnButton(); // Initial attempt to hook
// Observe DOM changes to hook buttons that might appear later
const observer = new MutationObserver(async (mutationsList, obs) => {
// console.log('[AutoSomi] MutationObserver (버튼 훅): DOM 변경 감지.');
hookDecodeTriggerOnButton(); // Try to hook any new buttons
await handleBlockingModals(host); // Also handle modals that might appear due to DOM changes
});
observer.observe(document.body, { childList: true, subtree: true });
console.log('[AutoSomi] Kone.gg 특화 로직 (버튼 훅) IIFE: MutationObserver 시작됨.');
})();
// Kone.gg specific logic for URL changes and content loading (IIFE)
(function () {
'use strict';
if (location.host !== 'kone.gg') return;
console.log('[AutoSomi] Kone.gg 특화 로직 (URL 변경 및 콘텐츠 로드 감지) IIFE 시작.');
let lastUrl = location.href;
let mainProcessingIntervalId = null; // Store the interval ID for content checking
// Observer for URL changes (simulated, as kone.gg might use SPA navigation)
const observer = new MutationObserver(async () => {
const currentUrl = location.href;
if (currentUrl !== lastUrl) {
console.log('[AutoSomi] Kone.gg URL 변경 감지:', lastUrl, '->', currentUrl);
lastUrl = currentUrl;
if (mainProcessingIntervalId) { // Clear previous interval if any
clearInterval(mainProcessingIntervalId);
console.log('[AutoSomi] Kone.gg URL 변경: 이전 observeAndRunKoneFunctions Interval 중지됨.');
}
await handleBlockingModals(host); // Handle modals on new "page"
observeAndRunKoneFunctions(); // Re-run content processing for the new page content
}
});
observer.observe(document.body, { childList: true, subtree: true }); // Observe for any changes that might indicate navigation
console.log('[AutoSomi] Kone.gg 특화 로직 (URL 변경 및 콘텐츠 로드 감지) IIFE: MutationObserver 시작됨.');
async function observeAndRunKoneFunctions() {
console.log('[AutoSomi] observeAndRunKoneFunctions: 함수 시작');
const start = Date.now();
const timeout = 8000; // Timeout for waiting for content (8 seconds)
if (mainProcessingIntervalId) { // Clear previous interval if any before starting a new one
clearInterval(mainProcessingIntervalId);
console.log('[AutoSomi] observeAndRunKoneFunctions: 이전 Interval 중지됨 (새로운 실행 전).');
}
mainProcessingIntervalId = setInterval(async () => {
// console.log('[AutoSomi] observeAndRunKoneFunctions: Interval 실행 중...');
await handleBlockingModals(host); // Periodically check for modals
const atc = getKoneGGContentElement(); // Get content element
const hasText = atc && (atc.textContent || '').length > 20; // Check if content has substantial text
const hasEncoded = atc && /aHR0c|YUhSMG|WVVoU|V1ZWb/.test(atc.textContent || ''); // Check for Base64
const hasDlsiteLink = atc && atc.querySelector('a[href*="dlsite.com"]'); // Check for existing DLsite links (might indicate content is loaded)
// If content is loaded (has text AND (has encoded strings OR has dlsite links))
if (atc && (hasText && (hasEncoded || hasDlsiteLink))) {
console.log('[AutoSomi] observeAndRunKoneFunctions: 콘텐츠 확인됨. Interval 중지 및 처리 시작.');
clearInterval(mainProcessingIntervalId);
mainProcessingIntervalId = null; // Reset interval ID
FindPW(); // Find passwords
setTimeout(() => { // Then decode and add DLsite links
doDec();
doDlsiteContextAware();
}, 300);
}
if (Date.now() - start > timeout) { // If timeout is reached
console.log('[AutoSomi] observeAndRunKoneFunctions: 시간 초과. Interval 중지.');
clearInterval(mainProcessingIntervalId);
mainProcessingIntervalId = null; // Reset interval ID
}
}, 300); // Check every 300ms
}
// Initial run when the script loads for kone.gg
(async () => {
await handleBlockingModals(host);
observeAndRunKoneFunctions();
})();
})();
내 글 최신(25년 05/23 기준)에 다시 수정한 거에서 동작하는 지 첨부 사진 (난 됨. 안되면 몰루! 패치 기다리면 됨.)
