|
2026-09-12 22:35
조회: 1,289
추천: 6
내 댓글 삭제 기록용(async function(){ 'use strict'; var Z=[10,3]; var GAP=200; var MAX_PAGE=200; var NL=String.fromCharCode(10); function sleep(ms){ return new Promise(function(resolve){ setTimeout(resolve,ms); }); } function log(){ var args=['[정리]'].concat(Array.prototype.slice.call(arguments)); console.log.apply(console,args); } function atLeast(value,limit){ return Math.max(value,limit)===value; } function isDigits(value){ var s=String(value==null?'':value).trim(); var i; var c; if(!s){ return false; } for(i=0;i<s.length;i++){ c=s.charCodeAt(i); if(c<48 || c>57){ return false; } } return true; } function compactText(value){ var s=String(value==null?'':value); var out=''; var spaced=false; var i; var c; for(i=0;i<s.length;i++){ c=s.charCodeAt(i); if(c===9 || c===10 || c===13 || c===32){ if(out && !spaced){ out+=' '; spaced=true; } }else{ out+=s.charAt(i); spaced=false; } } return out.trim(); } function cleanPathParts(pathname){ var raw=String(pathname||'').split('/'); var out=[]; var i; for(i=0;i<raw.length;i++){ if(raw[i]){ out.push(raw[i]); } } return out; } function idsFromUrl(value,base){ try{ var u=new URL(value,base||location.href); var parts=cleanPathParts(u.pathname); var index=parts.indexOf('board'); if(index===-1){ return null; } var boardId=parts[index+2]||''; var postId=parts[index+3]||''; if(!isDigits(boardId)){ return null; } return { boardId:boardId, postId:isDigits(postId)?postId:'' }; } catch(error){ return null; } } var currentUrl=new URL(location.href); if(currentUrl.searchParams.get('my')!=='opi'){ alert('내 댓글 목록 페이지에서 실행하세요.'); return; } var BOARD_ID=currentUrl.searchParams.get('come_idx')||''; if(!isDigits(BOARD_ID)){ var currentIds=idsFromUrl(location.href,location.href); if(currentIds){ BOARD_ID=currentIds.boardId; } } if(!isDigits(BOARD_ID)){ var linksForBoard=document.querySelectorAll('a[href]'); var boardLinkIndex; for(boardLinkIndex=0;boardLinkIndex<linksForBoard.length;boardLinkIndex++){ var boardIds=idsFromUrl( linksForBoard[boardLinkIndex].getAttribute('href'), location.href ); if(boardIds && boardIds.postId){ BOARD_ID=boardIds.boardId; break; } } } var userNode=document.querySelector('#header-userinfo .user-name'); var USER_NAME=userNode ? compactText(userNode.textContent) : ''; if(!isDigits(BOARD_ID) || !USER_NAME){ alert('게시판 번호 또는 로그인 닉네임을 확인할 수 없습니다.'); return; } var randomBytes=crypto.getRandomValues(new Uint8Array(16)); var CHK=''; var ri; for(ri=0;ri<randomBytes.length;ri++){ CHK+=randomBytes[ri].toString(16).padStart(2,'0'); } var ignored=new Set(); var deleted=0; var protectedCount=0; var skipped=0; var box=document.createElement('div'); box.style= 'position:fixed;right:15px;bottom:15px;z-index:999999;'+ 'background:#111;color:#fff;padding:12px 15px;'+ 'font:13px/1.55 monospace;border-radius:7px;'+ 'max-width:500px;white-space:pre-wrap;'; document.body.appendChild(box); function status(text){ box.textContent= '[댓글 정리]'+ NL+ text+ NL+ '삭제 '+deleted+ ' / 보호 '+protectedCount+ ' / 제외 '+skipped; } async function request(params){ var body=new URLSearchParams(); var key; body.append('comeidx',BOARD_ID); body.append('sortorder','date'); body.append('out','json'); body.append('replynick',''); body.append('replyidx','0'); body.append('chkcode',CHK); body.append('uploadurl',''); body.append('imageposition',''); body.append('videoloading','lazy'); for(key in params){ if(Object.prototype.hasOwnProperty.call(params,key)){ body.append(key,String(params[key])); } } var response=await fetch( '/common/board/comment.json.php?dummy='+Date.now(), { method:'POST', credentials:'include', headers:{ 'Accept':'application/json', 'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8' }, body:body } ); if(response.status===403 || response.status===429){ throw new Error( '서버 제한 감지 HTTP '+response.status ); } if(!response.ok){ throw new Error( 'HTTP '+response.status ); } return response.json(); } function addComments(target,data){ var groups=data.commentlist||[]; var gi; var ci; var list; var c; var id; for(gi=0;gi<groups.length;gi++){ list=groups[gi].list||[]; for(ci=0;ci<list.length;ci++){ c=list[ci]; id=''; if(c && c.__attr__ && c.__attr__.cmtidx){ id=String(c.__attr__.cmtidx); } if(id){ target.set(id,c); } } } } async function getComments(articleCode){ var all=new Map(); var first=await request({ articlecode:articleCode, act:'list' }); var total=Number(first.cmtcount)||0; var step=Number(first.pagecount)||100; var pages=Math.ceil(total/step); var pageIndex; addComments(all,first); for(pageIndex=1;pageIndex<pages;pageIndex++){ var next=await request({ articlecode:articleCode, act:'list', titles:String(pageIndex*step) }); addComments(all,next); } return Array.from(all.values()); } async function removeComment(articleCode,id){ var result=await request({ articlecode:articleCode, act:'del', cmtcodes:String(id) }); return Number(result.message)===1; } async function getList(page){ var u=new URL(location.href); u.searchParams.set('my','opi'); u.searchParams.set('p',String(page)); var response=await fetch( u.href, { credentials:'include', cache:'no-store' } ); if(response.status===403 || response.status===429){ throw new Error( '서버 제한 감지 HTTP '+response.status ); } if(!response.ok){ throw new Error( '목록 HTTP '+response.status ); } var html=await response.text(); var d=new DOMParser() .parseFromString( html, 'text/html' ); var posts=new Map(); var links= d.querySelectorAll( 'a.subject-link[href]' ); var linkIndex; for(linkIndex=0;linkIndex<links.length;linkIndex++){ var ids=idsFromUrl( links[linkIndex].getAttribute('href'), u.href ); if( ids && ids.boardId===String(BOARD_ID) && ids.postId ){ posts.set( ids.postId, { id:ids.postId, title: compactText( links[linkIndex].textContent )||ids.postId } ); } } if(!posts.size){ var allRows= d.querySelectorAll( 'tr' ); var rowIndex; for(rowIndex=0;rowIndex<allRows.length;rowIndex++){ var rowLinks= allRows[rowIndex] .querySelectorAll( 'a[href]' ); var rowLinkIndex; for(rowLinkIndex=0;rowLinkIndex<rowLinks.length;rowLinkIndex++){ var rowIds=idsFromUrl( rowLinks[rowLinkIndex].getAttribute('href'), u.href ); if( rowIds && rowIds.boardId===String(BOARD_ID) && rowIds.postId ){ posts.set( rowIds.postId, { id:rowIds.postId, title: compactText( rowLinks[rowLinkIndex].textContent )||rowIds.postId } ); break; } } } } return Array.from(posts.values()); } function plainText(html){ var temp= document.createElement( 'div' ); temp.innerHTML= String(html||''); return compactText( temp.textContent|| temp.innerText|| '' ).slice(0,80); } async function processPost(post){ status( '댓글 조회'+ NL+ post.title ); var comments= await getComments( post.id ); var mine=[]; var ci; for(ci=0;ci<comments.length;ci++){ if( compactText( comments[ci].o_name||'' )===USER_NAME ){ mine.push( comments[ci] ); } } mine.reverse(); var changed=0; var mi; for(mi=0;mi<mine.length;mi++){ var c=mine[mi]; var id=''; if( c && c.__attr__ && c.__attr__.cmtidx ){ id= String( c.__attr__.cmtidx ); } if( !id || ignored.has(id) ){ continue; } var recommend= Number( c.o_recommend ); var notrecommend= Number( c.o_notrecommend ); var dice=0; if(c.__attr__){ dice= Number( c.__attr__.dice )||0; } if( !Number.isFinite(recommend) || !Number.isFinite(notrecommend) || atLeast(recommend,Z[0]) || atLeast(notrecommend,Z[1]) || dice!==0 ){ ignored.add(id); protectedCount++; continue; } var text= plainText( c.o_comment ); status( '삭제 중'+ NL+ post.title+ NL+ text ); try{ if( await removeComment( post.id, id ) ){ deleted++; changed++; log( '삭제 완료 | '+ post.title+ ' | '+ text ); }else{ ignored.add(id); skipped++; log( '삭제 거부 | '+ post.title ); } } catch(error3){ if( String( error3.message ).indexOf('403')!==-1 || String( error3.message ).indexOf('429')!==-1 ){ throw error3; } ignored.add(id); skipped++; log( '삭제 실패 | '+ post.title+ ' | '+ error3.message ); } await sleep(GAP); } return changed; } try{ var page=1; var pageSignatures=new Set(); while(page<=MAX_PAGE){ status( page+ '페이지 목록 확인' ); var posts= await getList(page); if(!posts.length){ log( page+ '페이지 없음 → 종료' ); break; } posts.reverse(); var ids=[]; var si; for(si=0;si<posts.length;si++){ ids.push( posts[si].id ); } var sig= page+ '|'+ ids.join(','); log( page+ '페이지 | 게시글 '+ posts.length+ '개' ); var changed=0; var pi; for(pi=0;pi<posts.length;pi++){ status( page+ 'P '+ (pi+1)+ '/'+ posts.length+ NL+ posts[pi].title ); try{ changed+= await processPost( posts[pi] ); } catch(error4){ if( String( error4.message ).indexOf('403')!==-1 || String( error4.message ).indexOf('429')!==-1 ){ throw error4; } skipped++; log( '게시글 실패 | '+ posts[pi].title+ ' | '+ error4.message ); } } if(changed!==0){ log( page+ '페이지 '+ changed+ '개 삭제 → 재확인' ); continue; } if( pageSignatures.has(sig) ){ log( page+ '페이지 변화 없음' ); } pageSignatures.add(sig); log( page+ '페이지 삭제 가능 댓글 없음 → '+ (page+1)+ '페이지' ); page++; } status('전체 완료'); log( '===== 전체 완료 =====' ); log( '삭제 '+deleted ); log( '보호 '+protectedCount ); log( '삭제 불가/실패 '+ skipped ); } catch(error5){ status( '중단'+ NL+ error5.message ); log( '===== 자동 중단 ===== | '+ error5.message ); } })(); 그곳이 터져 죽습니다. 대화 그만하기 거절하기
|
메이플스토리 인벤 자유 게시판 게시판
인벤 전광판
[그녀와침대로] ANG~♥♥♥♥♥♥
메이플스토리 인벤에 오신 것을 환영합니다~★
[플위유] 안눙허세요
[토리하] 와헌=세이람
[예저] 윈브를 살려내라
[냥이가최고] 라라 살려주세요 제발여ㅠㅠ
