您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
comment_allegation/* で getthumbinfo を参照し、ロック済み×WL一致のタグを本文先頭へ。
// ==UserScript== // @name Nicon Tag Allegation Autofill // @namespace https://greasyfork.org/users/prozent55 // @version 1.1.1 // @description comment_allegation/* で getthumbinfo を参照し、ロック済み×WL一致のタグを本文先頭へ。 // @match https://www.nicovideo.jp/comment_allegation/* // @run-at document-idle // @connect ext.nicovideo.jp // @grant GM.xmlHttpRequest // @grant GM_xmlHttpRequest // @license MIT // ==/UserScript== (() => { 'use strict'; const DEFAULT_TARGET='tag', DEFAULT_ITEM='search_interference'; const DEFAULT_REASON_BODY='動画の内容とは無関係なタグがロックされており、削除できません。\nタグ検索や関連機能に支障をきたすため、荒らし行為にあたり利用規約違反の可能性があると判断しました。'; const WL_KEY='zr_tag_wl_v1', REASON_KEY='zr_reason_body_v1'; const WL_DEFAULT=['真夏の夜の淫夢','淫夢実況シリーズ','ひとくち淫夢','淫夢本編リンク','本編改造淫夢','BB先輩シリーズ','ホラー淫夢','タクヤさん']; const loadWL=()=>{try{const s=localStorage.getItem(WL_KEY);return s?JSON.parse(s):WL_DEFAULT.slice();}catch{return WL_DEFAULT.slice();}}; const saveWL=(arr)=>{try{localStorage.setItem(WL_KEY,JSON.stringify(arr||[]));}catch{}}; const getReason=()=>{try{const s=localStorage.getItem(REASON_KEY);return s??DEFAULT_REASON_BODY;}catch{return DEFAULT_REASON_BODY;}}; const setReason=(txt)=>{try{(txt==null||txt==='')?localStorage.removeItem(REASON_KEY):localStorage.setItem(REASON_KEY,String(txt));}catch{}}; const KP='zippy_nico_tag_form_',K_TARGET=KP+'target',K_ITEM=KP+'item',K_TEXT=KP+'text'; const storage={async get(k,d){try{const v=localStorage.getItem('__'+k);return v==null?d:JSON.parse(v);}catch{return d;}},async set(k,v){try{localStorage.setItem('__'+k,JSON.stringify(v));}catch{}}}; const norm=s=>(s||'').trim().toLowerCase(); const qs=(s,r=document)=>r.querySelector(s); const qsa=(s,r=document)=>Array.from(r.querySelectorAll(s)); const fire=(el)=>{if(!el)return;el.dispatchEvent(new Event('input',{bubbles:true}));el.dispatchEvent(new Event('change',{bubbles:true}));}; function setVal(el,val){const proto=el?.constructor?.prototype||HTMLTextAreaElement.prototype;const d=proto&&Object.getOwnPropertyDescriptor(proto,'value');d?.set?d.set.call(el,val):el.value=val;fire(el);} function toast(msg,ms=1200){const n=document.createElement('div');n.textContent=msg;Object.assign(n.style,{position:'fixed',right:'16px',bottom:'110px',zIndex:999999,background:'#00c853',color:'#fff',padding:'8px 10px',borderRadius:'8px',boxShadow:'0 4px 12px rgba(0,0,0,.25)',opacity:'0',transition:'opacity .15s'});document.body.appendChild(n);requestAnimationFrame(()=>n.style.opacity='1');setTimeout(()=>{n.style.opacity='0';setTimeout(()=>n.remove(),180);},ms);} function waitForForm(ms=8000){return new Promise(res=>{const pick=()=>{const radios=qsa('input[type="radio"][name="target"]');const select=qs('select[name="select_allegation"]');const ta=qs('textarea[name="inquiry"]#inquiry');return(radios.length&&select&&ta)?{radios,select,ta}:null;};const first=pick();if(first)return res(first);const to=setTimeout(()=>{mo.disconnect();res(null);},ms);const mo=new MutationObserver(()=>{const f=pick();if(f){clearTimeout(to);mo.disconnect();res(f);}});mo.observe(document.body,{childList:true,subtree:true});});} function videoIdFromPath(){const m=location.pathname.match(/\/comment_allegation\/([a-z]{2}\d+)/i);return m?m[1]:null;} function httpGet(url){return new Promise((resolve,reject)=>{const fn=(typeof GM?.xmlHttpRequest==='function')?GM.xmlHttpRequest:(typeof GM_xmlhttpRequest==='function')?GM_xmlHttpRequest:null;if(!fn)return reject(new Error('GM.xmlHttpRequest not available'));fn({method:'GET',url,onload:r=>resolve(r.responseText),onerror:reject});});} async function fetchTags(videoId){if(!videoId)return[];const url=`https://ext.nicovideo.jp/api/getthumbinfo/${encodeURIComponent(videoId)}`;let xml='';try{xml=await httpGet(url);}catch{return[];}let doc;try{doc=new DOMParser().parseFromString(xml,'text/xml');}catch{return[];}const nodes=Array.from(doc.querySelectorAll('thumb > tags > tag, tags > tag'));return nodes.map(t=>({name:(t.textContent||'').trim(),locked:(t.getAttribute('lock')==='1'||t.getAttribute('locked')==='1')})).filter(x=>x.name);} function filterWLAllLocked(thumbTags,wl){const src=thumbTags.filter(t=>t.locked).map(t=>t.name);if(!src.length||!wl.length)return[];const S=src.map(norm);const out=[];for(const w of wl){const i=S.indexOf(norm(w));if(i!==-1&&!out.includes(src[i]))out.push(src[i]);}return out;} const TAG_LINE_RE=/^【タグの内容】.*(?:\r?\n)?/m; function composeWithTagLine(currentText,tags,reason){const body0=(currentText||'').replace(TAG_LINE_RE,'');const tagLine=`【タグの内容】\n${tags.length?tags.join('、'):'(未特定)'}\n`;const body=body0.trim()?body0.replace(/^\r?\n+/,''):`【違反と判断された理由】\n${reason}`;return tagLine+body;} // ---- textareaモーダル ---- function openTextareaModal(title,initial,onSave){ const overlay=document.createElement('div'); Object.assign(overlay.style,{position:'fixed',top:0,left:0,right:0,bottom:0,background:'rgba(0,0,0,.4)',zIndex:1000000,display:'flex',alignItems:'center',justifyContent:'center'}); const box=document.createElement('div'); Object.assign(box.style,{background:'#fff',padding:'12px',borderRadius:'8px',width:'480px',maxWidth:'90%',display:'flex',flexDirection:'column'}); const h=document.createElement('div');h.textContent=title;Object.assign(h.style,{marginBottom:'8px',fontWeight:'bold'});box.appendChild(h); const ta=document.createElement('textarea');ta.value=initial;Object.assign(ta.style,{flex:'1',minHeight:'160px',font:'13px monospace',marginBottom:'8px'});box.appendChild(ta); const row=document.createElement('div');Object.assign(row.style,{textAlign:'right'}); const ok=document.createElement('button');ok.textContent='保存';ok.onclick=()=>{onSave(ta.value);document.body.removeChild(overlay);}; const cancel=document.createElement('button');cancel.textContent='キャンセル';cancel.style.marginLeft='6px';cancel.onclick=()=>document.body.removeChild(overlay); row.append(ok,cancel);box.appendChild(row); overlay.appendChild(box);document.body.appendChild(overlay); } function panel(nodes,reapply){ if(document.getElementById('zr-min2-host'))return; const host=document.createElement('div');host.id='zr-min2-host';Object.assign(host.style,{position:'fixed',right:'16px',bottom:'16px',zIndex:999999});document.body.appendChild(host); const root=host.attachShadow({mode:'open'}); const wrap=document.createElement('div');wrap.className='zr-min2'; wrap.innerHTML=` <div class="row"><b>自動入力(ローカル⇄既定)</b></div> <div class="row"> <button id="zr-edit-wl" type="button">タグ編集</button> <button id="zr-edit-reason" type="button">理由文編集</button> <button id="zr-reset" type="button">既定に戻す</button> </div>`; const style=document.createElement('style');style.textContent=` :host{all:initial;} .zr-min2{all:initial;display:block;font:12px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Noto Sans JP","Hiragino Kaku Gothic ProN",Meiryo,sans-serif;color:#fff;background:#0b1220cc;backdrop-filter:blur(6px);padding:10px 12px;border-radius:10px;box-shadow:0 8px 20px rgba(0,0,0,.35);} .row{all:initial;display:block;margin:6px 0;font:inherit;color:inherit;} b{all:initial;font:inherit;font-weight:700;color:inherit;} button{all:initial;font:inherit;color:#fff;background:#1f6feb;padding:6px 10px;border-radius:6px;cursor:pointer;margin-right:6px;box-shadow:0 1px 2px rgba(0,0,0,.25);} button:hover{background:#2b7af3;}button:active{background:#195bd0;}#zr-reset{background:#d93025;} `; root.append(style,wrap); root.getElementById('zr-edit-wl').addEventListener('click',()=>{ const cur=JSON.stringify(loadWL(),null,2); const nxt=prompt('ホワイトリスト(JSON配列):',cur); if(!nxt)return; try{saveWL(JSON.parse(nxt));reapply({forceDefaultBody:false});toast('タグリストを保存しました');} catch{alert('JSONが不正です');} }); root.getElementById('zr-edit-reason').addEventListener('click',()=>{ openTextareaModal('理由文を編集',getReason(),(val)=>{setReason(val);reapply({forceDefaultBody:true});toast('理由文を保存しました');}); }); root.getElementById('zr-reset').addEventListener('click',()=>{ saveWL(WL_DEFAULT.slice());setReason('');reapply({forceDefaultBody:true});toast('既定に戻しました'); }); } (async function main(){ const nodes=await waitForForm();if(!nodes)return; const {radios,select,ta}=nodes; const savedTarget=await storage.get(K_TARGET,''),savedItem=await storage.get(K_ITEM,''),savedText=await storage.get(K_TEXT,''); const target=savedTarget||DEFAULT_TARGET,item=savedItem||DEFAULT_ITEM; const r=radios.find(x=>x.value===String(target));if(r){r.checked=true;fire(r);} if([...select.options].some(o=>o.value===item)){select.value=item;fire(select);} const videoId=videoIdFromPath();const thumbTags=await fetchTags(videoId); const applyNow=({forceDefaultBody=false}={})=>{ const wl=loadWL(),hit=filterWLAllLocked(thumbTags,wl); const cur=forceDefaultBody?'':(savedText||ta.value||''); const next=composeWithTagLine(cur,hit,getReason()); setVal(ta,next); const check=ta.value||'';const ensured=check.replace(/^【タグの内容】[^\r\n]*\r?\n?/,m=>m.endsWith('\n')?m:(m+'\n'));if(ensured!==check)setVal(ta,ensured); }; applyNow();panel(nodes,applyNow); })(); })();