|
2026-09-12 22:34
조회: 119
추천: 0
내 글 삭제 기록용(async function(){ 'use strict'; var Z=[10,10]; var GAP=300; var VERIFY_GAP=700; var PAGE_GAP=400; 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 compactNoSpace(value){ var s=String(value==null?'':value); var out=''; var i; var c; for(i=0;i<s.length;i++){ c=s.charCodeAt(i); if(c!==9 && c!==10 && c!==13 && c!==32){ out+=s.charAt(i); } } return out.trim(); } function num(value){ var s=String(value==null?'':value).split(',').join('').trim(); var sign=1; var started=false; var digits=''; var i; var c; for(i=0;i<s.length;i++){ c=s.charCodeAt(i); if(!started && s.charAt(i)==='-'){ sign=-1; started=true; continue; } if(c>=48 && c<=57){ digits+=s.charAt(i); started=true; }else if(digits){ break; } } if(!digits){ return NaN; } return sign*Number(digits); } 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')!=='post'){ 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; } } } if(!isDigits(BOARD_ID)){ alert('게시판 번호를 확인할 수 없습니다.'); return; } var ignored=new Set(); var deleted=0; var protectedCount=0; var skipped=0; window.__INVEN_POST_DELETE_STOP__=false; 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; } function checkStop(){ if(window.__INVEN_POST_DELETE_STOP__){ throw new Error( '사용자가 작업을 중지했습니다.' ); } } async function request(url,options){ checkStop(); var opts=options||{}; opts.credentials='include'; opts.cache='no-store'; var response=await fetch(url,opts); if( response.status===403 || response.status===429 ){ throw new Error( '서버 제한 감지 HTTP '+ response.status ); } if(!response.ok){ throw new Error( 'HTTP '+ response.status ); } return response; } function findHeaderIndex(headers,name){ var i; var text; for(i=0;i<headers.length;i++){ text=compactNoSpace( headers[i].textContent ); if(text===name){ return i; } } for(i=0;i<headers.length;i++){ text=compactNoSpace( headers[i].textContent ); if( name==='추천' && text.indexOf('비추천')!==-1 ){ continue; } if(text.indexOf(name)!==-1){ return i; } } return -1; } function postIdFromRow(row,baseUrl){ var numCell= row.querySelector( 'td.num' ); var raw=''; if(numCell){ var span= numCell.querySelector( 'span' ); raw=compactText( span ? span.textContent : numCell.textContent ); if(isDigits(raw)){ return raw; } } var links= row.querySelectorAll( 'a[href]' ); var i; for(i=0;i<links.length;i++){ var ids=idsFromUrl( links[i].getAttribute('href'), baseUrl ); if( ids && ids.boardId===String(BOARD_ID) && ids.postId ){ return ids.postId; } } return ''; } function subjectFromRow(row,id,baseUrl){ var subject= row.querySelector( 'a.subject-link[href]' ); if(subject){ return subject; } var links= row.querySelectorAll( 'a[href]' ); var i; for(i=0;i<links.length;i++){ var ids=idsFromUrl( links[i].getAttribute('href'), baseUrl ); if( ids && ids.boardId===String(BOARD_ID) && ids.postId===String(id) ){ return links[i]; } } return null; } async function getList(page){ var u=new URL(location.href); u.searchParams.set( 'my', 'post' ); u.searchParams.set( 'p', String(page) ); var response= await request( u.href ); var html= await response.text(); var d= new DOMParser() .parseFromString( html, 'text/html' ); var posts= new Map(); var tables= d.querySelectorAll( 'table' ); var tableIndex; for( tableIndex=0; tableIndex<tables.length; tableIndex++ ){ var table= tables[tableIndex]; var headers= table.querySelectorAll( 'thead th' ); var recIndex= findHeaderIndex( headers, '추천' ); var downIndex= findHeaderIndex( headers, '비추천' ); var rows= table.querySelectorAll( 'tbody tr' ); var rowIndex; for( rowIndex=0; rowIndex<rows.length; rowIndex++ ){ var row= rows[rowIndex]; var id= postIdFromRow( row, u.href ); if(!isDigits(id)){ continue; } var subject= subjectFromRow( row, id, u.href ); if(!subject){ continue; } var cells= row.querySelectorAll( 'td' ); var recommend=NaN; var notrecommend=NaN; if( recIndex!==-1 && cells[recIndex] ){ recommend= num( cells[ recIndex ].textContent ); } if( downIndex!==-1 && cells[downIndex] ){ notrecommend= num( cells[ downIndex ].textContent ); } if( !Number.isFinite( recommend ) ){ var recCell= row.querySelector( 'td.recommend,td.recom,td.rec' ); if(recCell){ recommend= num( recCell.textContent ); } } if( !Number.isFinite( notrecommend ) ){ var downCell= row.querySelector( 'td.notrecommend,td.not-recommend,td.down,td.dislike' ); if(downCell){ notrecommend= num( downCell.textContent ); } } posts.set( id, { id:id, title: compactText( subject.textContent )||id, recommend: recommend, notrecommend: notrecommend } ); } } if(!posts.size){ var allRows= d.querySelectorAll( 'tr' ); var allRowIndex; for( allRowIndex=0; allRowIndex<allRows.length; allRowIndex++ ){ var row2= allRows[ allRowIndex ]; var id2= postIdFromRow( row2, u.href ); if(!isDigits(id2)){ continue; } var subject2= subjectFromRow( row2, id2, u.href ); if(!subject2){ continue; } var rec2=NaN; var down2=NaN; var rc= row2.querySelector( 'td.recommend,td.recom,td.rec' ); var dc= row2.querySelector( 'td.notrecommend,td.not-recommend,td.down,td.dislike' ); if(rc){ rec2= num( rc.textContent ); } if(dc){ down2= num( dc.textContent ); } posts.set( id2, { id:id2, title: compactText( subject2.textContent )||id2, recommend: rec2, notrecommend: down2 } ); } } return Array.from( posts.values() ); } async function removePost(id,page){ var body= new FormData(); body.append( 'come_idx', BOARD_ID ); body.append( 'p', String(page) ); body.append( 'l', String(id) ); body.append( 'my', 'post' ); await request( '/board/bbs/include/multi_delete.php', { method:'POST', headers:{ 'X-Requested-With': 'XMLHttpRequest' }, body:body } ); return true; } try{ var page=1; while(page<=MAX_PAGE){ checkStop(); status( page+ '페이지 목록 확인' ); var posts= await getList(page); if(!posts.length){ log( page+ '페이지 없음 → 종료' ); break; } posts.reverse(); log( page+ '페이지 | 게시글 '+ posts.length+ '개' ); var attempted=[]; var i; for( i=0; i<posts.length; i++ ){ checkStop(); var post= posts[i]; if( ignored.has( post.id ) ){ continue; } if( !Number.isFinite( post.recommend ) || !Number.isFinite( post.notrecommend ) || atLeast( post.recommend, Z[0] ) || atLeast( post.notrecommend, Z[1] ) ){ ignored.add( post.id ); protectedCount++; log( '보호 | '+ post.id+ ' | '+ post.title ); continue; } status( page+ 'P '+ (i+1)+ '/'+ posts.length+ NL+ post.title ); try{ await removePost( post.id, page ); attempted.push( post ); } catch(error3){ if( String( error3.message ).indexOf('403')!==-1 || String( error3.message ).indexOf('429')!==-1 ){ throw error3; } ignored.add( post.id ); skipped++; log( '삭제 실패 | '+ post.title+ ' | '+ error3.message ); } await sleep(GAP); } if(attempted.length){ status( page+ '페이지 삭제 결과 확인' ); await sleep( VERIFY_GAP ); var after= await getList(page); var remaining= new Set(); var ai; for( ai=0; ai<after.length; ai++ ){ remaining.add( after[ai].id ); } var changed=0; var ti; for( ti=0; ti<attempted.length; ti++ ){ var target= attempted[ti]; if( !remaining.has( target.id ) ){ deleted++; changed++; log( '삭제 완료 | '+ target.title ); }else{ ignored.add( target.id ); skipped++; log( '삭제 거부 | '+ target.title ); } } if(changed!==0){ log( page+ '페이지 '+ changed+ '개 삭제 → 재확인' ); continue; } } log( page+ '페이지 삭제 가능 게시글 없음 → '+ (page+1)+ '페이지' ); page++; await sleep( PAGE_GAP ); } status( '전체 완료' ); log( '===== 전체 완료 =====' ); log( '삭제 '+deleted ); log( '보호 '+protectedCount ); log( '삭제 불가/실패 '+ skipped ); } catch(error4){ status( '중단'+ NL+ error4.message ); log( '===== 자동 중단 ===== | '+ error4.message ); } })(); 그곳이 터져 죽습니다. 대화 그만하기 거절하기
|
메이플스토리 인벤 자유 게시판 게시판
인벤 전광판
[그녀와침대로] ANG~♥♥♥♥♥♥
[플위유] 안눙허세요
[냥이가최고] 라라 살려주세요 제발여ㅠㅠ
[토리하] 와헌=세이람
메이플스토리 인벤에 오신 것을 환영합니다~★
[예저] 윈브를 살려내라
