コピペレス検出用ブックマークレット20251224 既存レスのコピペを強調表示します 表示されるdelボタンはクリックで削除依頼を送信できます 並列化には未対応 ログでも動作します ・ナニコレ このブックマークレットは掲示板のレスを監視し過去のレスと重複している文章を自動で強調表示します 下にあるブックマークレットのコード(1行のみ)のどちらかをコピペして使います 1つ目のブックマークレットは特定のスレを対象とするため引用記号を無視して判定します 赤色は完全一致オレンジ色は部分一致を示します 各レスに「del」ボタンを表示しクリックで削除依頼を送信できます 検出件数は画面右下に表示され赤オレンジ別に確認可能です 動作が不安定な場合はページを更新するかブラウザを再起動すると直ります ・動作環境 PC用ブラウザ推奨(Chrome Edge Firefoxなど) スマホ用ブラウザは Firefox のみ動作確認済み スマホ用 Chrome 系ブラウザでは動作しません ふたクロ拡張機能を ON にした状態でも動作可能 ・使い方 1. 下のコードをコピーする 2. ブラウザでブックマークを作成する 3. ブックマークの URL にコピーしたコードを貼り付ける 4. 掲示板のスレッドページでブックマークをクリックする 5. ページ内のレスが解析され重複レスは色付きで強調表示される 6. 各レスの「del」ボタンを押すと削除依頼を送信可能 7. 右下のバッジで赤オレンジの検出件数を確認できる ・コードの要件・仕様 ・レスの本文は
内の文字列を取得 ・HTML タグ除去 空白正規化 不要文字列削除後に比較 ・過去に検出された文字列を seen オブジェクトで管理 ・完全一致(赤)と部分一致(オレンジ)で色を変更 ・右下固定のバッジで検出件数をリアルタイム表示 ・「del」ボタンは各レスに追加されクリックで削除依頼を送信 ・del 成功時のみボタンの色を緑に変更 ・追加読み込み分のレスも MutationObserver によって自動処理 ・文字コードは Shift_JIS を正しくデコードして表示 ・ブックマークレット実行中は window.__cpRunningForTab による二重実行防止 ・必要に応じて完全一致部分一致の判定文字数(デフォルト6文字)を変更可能 ・削除依頼の response に応じて alert 表示(成功時は「登録しました」) ・短時間で連続してdelした場合もボタンは押せる仕様 ・複数タブで同時に使用しても他タブに影響しない ・ブックマークレットのコード(特定スレ用・1行のみ) javascript:(()=>{if(window.__cpRunningForTab)return;window.__cpRunningForTab=1;let red=0,yel=0;const badge=document.createElement("div"),upd=()=>{badge.textContent=(red||yel)?"🟥"+red+" 🟨"+yel:"🔍 コピペ監視中"};Object.assign(badge.style,{position:"fixed",right:"8px",bottom:"8px",padding:"6px 10px",fontSize:"12px",background:"rgba(0,0,0,0.75)",color:"#fff",borderRadius:"6px",zIndex:999999,pointerEvents:"none"});upd();document.body.appendChild(badge);const seen={},NG=["スレッドを立てた人によって削除されました","削除依頼によって隔離されました","キタ━━━━━━(゚∀゚)━━━━━━ !!!!!"],norm=s=>s.replace(/<[^>]+>/g,"").replace(/\s+/g," ").trim(),split=b=>b.innerHTML.split(/
/i),mark=(r,t)=>{r.style.background=t=="full"?"rgba(255,0,0,0.2)":"rgba(255,165,0,0.25)";r.style.border=t=="full"?"2px solid red":"2px solid orange"};const addDel=r=>{if(r.dataset.delbtn)return;r.dataset.delbtn=1;const sod=r.querySelector(".sod"),cno=r.querySelector(".cno");if(!sod||!cno)return;const m=cno.textContent.match(/No\.(\d+)/);if(!m)return;const no=m[1];const d=document.createElement("button");d.textContent="del";Object.assign(d.style,{marginLeft:"4px",cursor:"pointer",fontSize:"12px",color:"#000",background:"#fff",border:"1px solid #000",borderRadius:"3px",padding:"1px 4px"});d.onclick=e=>{e.stopPropagation();fetch("https://img.2chan.net/del.php",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({mode:"post",b:"b",d:no,reason:"110",responsemode:"ajax"}),credentials:"include"}).then(r=>r.arrayBuffer()).then(buf=>{const t=new TextDecoder("shift_jis").decode(buf).trim();if(t=="ok"){alert("登録しました");d.style.background="#0a0";d.style.color="#fff";d.disabled=true}else alert(t)}).catch(_=>{alert("del送信失敗")})};sod.after(d)};const proc=r=>{if(!r||r.dataset.cp)return;r.dataset.cp=1;addDel(r);const b=r.querySelector("blockquote");if(!b)return;let raw=split(b),lines=raw.map(norm).filter(l=>l&&!NG.includes(l)),fk=null,pk=null;lines.forEach(l=>{if(l.length>=6&&seen[l])fk=l});if(!fk){lines.forEach(l=>{if(l.length>=6)for(const k in seen)if(k.length>=6&&l.includes(k)){pk=k;break}})}if(fk||pk){raw=raw.map(h=>{const t=norm(h);if(fk&&t===fk)return""+h+"";if(pk&&t.includes(pk))return h.replace(pk,""+pk+"");return h});b.innerHTML=raw.join("
");mark(r,fk?"full":"part");fk?red++:yel++;upd()}lines.forEach(l=>{if(l.length>=6)seen[l]=1})};document.querySelectorAll(".rtd").forEach(proc);const t=document.querySelector(".thre");t&&new MutationObserver(m=>{m.forEach(x=>x.addedNodes.forEach(n=>{if(n.nodeType==1){n.matches&&n.matches(".rtd")&&proc(n);n.querySelectorAll&&n.querySelectorAll(".rtd").forEach(proc)}}))}).observe(t,{childList:true,subtree:true});console.log("copipe watcher started")})(); ・ブックマークレットのコード(通常スレ用・1行のみ) javascript:(()=>{if(window.__cpRunningForTab)return;window.__cpRunningForTab=1;let red=0,yel=0;const badge=document.createElement("div"),upd=()=>{badge.textContent=(red||yel)?"🟥"+red+" 🟨"+yel:"🔍 コピペ監視中"};Object.assign(badge.style,{position:"fixed",right:"8px",bottom:"8px",padding:"6px 10px",fontSize:"12px",background:"rgba(0,0,0,0.75)",color:"#fff",borderRadius:"6px",zIndex:999999,pointerEvents:"none"});upd();document.body.appendChild(badge);const seen={},NG=["スレッドを立てた人によって削除されました","削除依頼によって隔離されました","キタ━━━━━━(゚∀゚)━━━━━━ !!!!!"],isQuote=t=>/^\s*(?:>|>|>)+/.test(t),norm=s=>s.replace(/<[^>]+>/g,"").replace(/>/g,">").replace(/\s+/g," ").trim(),split=b=>b.innerHTML.split(/
/i),mark=(r,t)=>{r.style.background=t=="full"?"rgba(255,0,0,0.2)":"rgba(255,165,0,0.25)";r.style.border=t=="full"?"2px solid red":"2px solid orange"};const addDel=r=>{if(r.dataset.delbtn)return;r.dataset.delbtn=1;const sod=r.querySelector(".sod"),cno=r.querySelector(".cno");if(!sod||!cno)return;const m=cno.textContent.match(/No\.(\d+)/);if(!m)return;const no=m[1];const d=document.createElement("button");d.textContent="del";Object.assign(d.style,{marginLeft:"4px",cursor:"pointer",fontSize:"12px",color:"#000",background:"#fff",border:"1px solid #000",borderRadius:"3px",padding:"1px 4px"});d.onclick=e=>{e.stopPropagation();fetch("https://img.2chan.net/del.php",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({mode:"post",b:"b",d:no,reason:"110",responsemode:"ajax"}),credentials:"include"}).then(r=>r.arrayBuffer()).then(buf=>{const t=new TextDecoder("shift_jis").decode(buf).trim();if(t=="ok"){alert("登録しました");d.style.background="#0a0";d.style.color="#fff";d.disabled=true}else alert(t)}).catch(_=>{alert("del送信失敗")})};sod.after(d)};const getLines=r=>{const b=r.querySelector("blockquote");if(!b)return[];return split(b).map(norm).filter(l=>l&&!NG.includes(l)&&!isQuote(l))};const proc=r=>{if(!r||r.dataset.cp)return;r.dataset.cp=1;addDel(r);const lines=getLines(r);let fk=null,pk=null;lines.forEach(l=>{if(l.length>=6&&seen[l])fk=l});if(!fk){lines.forEach(l=>{if(l.length>=6)for(const k in seen)if(k.length>=6&&l.includes(k)){pk=k;break}})}if(fk||pk){let raw=split(r.querySelector("blockquote"));raw=raw.map(h=>{const t=norm(h);if(fk&&t===fk)return""+h+"";if(pk&&t.includes(pk))return h.replace(pk,""+pk+"");return h});r.querySelector("blockquote").innerHTML=raw.join("
");mark(r,fk?"full":"part");fk?red++:yel++;upd()}lines.forEach(l=>{if(l.length>=6)seen[l]=1})};document.querySelectorAll(".rtd").forEach(proc);const t=document.querySelector(".thre");t&&new MutationObserver(m=>{m.forEach(x=>x.addedNodes.forEach(n=>{if(n.nodeType==1){n.matches&&n.matches(".rtd")&&proc(n);n.querySelectorAll&&n.querySelectorAll(".rtd").forEach(proc)}}))}).observe(t,{childList:true,subtree:true});console.log("copipe watcher started (quote-safe)")})();