备份现场结果补录、图片修复与跳过文章功能

This commit is contained in:
Longtail committed 2026-10-31 17:08:49 +08:00
1 parent 1e6504b4a2
commit d157c179c8
20 files changed
+597 -47

No files matched your search

+42 -5
View File
@@ -7,6 +7,7 @@
const owner=sessionStorage.getItem('auto-news-owner') || crypto.randomUUID();
sessionStorage.setItem('auto-news-owner',owner);
let run=null, working=false, halted=true, aborter=null, active=null, tickBusy=false;
let skipBusy=false, preparation=null;
const pending=new Map();
function rpc(route,body={}) {
return new Promise((resolve,reject)=>{
@@ -48,7 +49,7 @@
const body=document.querySelector('#e_iframe')?.contentDocument?.body;
if(!body)throw new Error('找不到可视化编辑器');
return {title:document.querySelector('#subject')?.value || '',
images:[...body.querySelectorAll('img')].map(x=>({id:x.id,src:x.getAttribute('src')})),
images:[...body.querySelectorAll('img')].map(x=>({id:x.id,aid:x.getAttribute('aid'),src:x.getAttribute('src')})),
// Snapshot only final submitted content; never includes hidden tokens or the full form.
html:body.innerHTML.slice(0,500000), text:body.innerText.slice(0,100000)};
}
@@ -56,6 +57,7 @@
function loadLocal(){try{return JSON.parse(sessionStorage.getItem(SLOT)||'null');}catch{return null;}}
async function event(state,extra={}) {return rpc('event',{id:active.id,state,epoch:run.epoch,...extra});}
async function fail(error) {
if(skipBusy)return;
status(error.message);
if(active)try{await event('needs_attention',{error:error.message});}catch{}
halted=true;aborter?.abort();document.dispatchEvent(new CustomEvent('dww11:auto-stop'));
@@ -84,8 +86,9 @@
window.DWW11Formatter.format(job.source);assertLive(epoch);
const snapshot=editorSnapshot();
if(snapshot.title!==job.title)throw new Error('标题校验失败');
const aids=new Set(Object.values(result.images));
for(const aid of aids) if(!snapshot.images.some(x=>x.id==='attachimg_'+aid))throw new Error('配图附件缺失:'+aid);
await window.DWW11Importer.verifyEditorImages(
document.querySelector('#e_iframe').contentDocument.body, result.attachmentOrder, aborter.signal);
assertLive(epoch);
await event('awaiting_user_review',{snapshot});assertLive(epoch);
active={...job,state:'awaiting_user_review'};saveLocal({id:job.id});
status('已完成排版,请核对内容并亲手点击“发表帖子”。');
@@ -93,6 +96,9 @@
}
// Record only trusted user activation. Never replay a click, call submit(), or send a post request.
document.addEventListener('click',event=>{
if(skipBusy&&event.target.closest?.('#postsubmit')) {
event.preventDefault();event.stopImmediatePropagation();return;
}
if(!event.isTrusted||!event.target.closest?.('#postsubmit')||active?.state!=='awaiting_user_review')return;
try {
const snapshot=editorSnapshot();
@@ -103,6 +109,35 @@
active.state='user_submitting';status('已观察到人工点击,等待结果页核对。');
}catch(error){status('提交快照记录失败:'+error.message);}
},true);
document.addEventListener('submit',event=>{
if(skipBusy&&event.target.id==='postform') {event.preventDefault();event.stopImmediatePropagation();}
},true);
document.addEventListener('dww11:skip-article',async()=>{
if(skipBusy)return;
if(!active||!run||run.owner!==owner) {status('当前标签没有可跳过的文章');return;}
if(!['preparing','importing','formatting','awaiting_user_review','needs_attention'].includes(active.state)) {
status('文章已提交或结果待核对,请先人工核对');return;
}
skipBusy=true;halted=true;
document.dispatchEvent(new CustomEvent('dww11:skip-busy',{detail:true}));
const jobId=active.id,epoch=run.epoch;
aborter?.abort(new Error('正在跳过当前文章'));
status('正在跳过当前文章…');
try {
// Let cancelled imports finish their rollback before leaving the editor.
await preparation;
const result=await rpc('skip',{id:jobId,epoch});
sessionStorage.removeItem(SLOT);active=null;run=result.run;
status('已跳过,正在打开下一篇文章的发帖页…');
// Always load a fresh editor, including when the fid and URL are unchanged.
location.assign(`https://www.dww11.com/forum.php?mod=post&action=newthread&fid=${result.fid}`);
}catch(error){
try{await rpc('stop');}catch{}
status('跳过未完成,流程已暂停:'+error.message);
skipBusy=false;
document.dispatchEvent(new CustomEvent('dww11:skip-busy',{detail:false}));
}
});
async function observeResult(job) {
const local=loadLocal();
if(local?.id===job.id&&local.snapshot&&job.state==='awaiting_user_review') {
@@ -130,9 +165,10 @@
button.click();status('正在换号;等待插件完成并核对新账号');
}
async function tick() {
if(tickBusy)return;tickBusy=true;
if(tickBusy||skipBusy)return;tickBusy=true;
try {
run=await rpc('heartbeat',{account:account(),url:location.href,extension:extension()});
if(skipBusy)return;
halted=!run.running;
if(!run.running) {aborter?.abort();document.dispatchEvent(new CustomEvent('dww11:auto-stop'));}
if(working)return;
@@ -146,8 +182,9 @@
status(run.reason || '流程已暂停');return;
}
const next=await rpc('next',{account:account(),extension:extension()});run=next.run;
if(skipBusy)return;
if(next.job)active=next.job;
if(next.action==='prepare'||(next.action==='resume'&&next.job.state==='preparing'))void prepare(next.job);
if(next.action==='prepare'||(next.action==='resume'&&next.job.state==='preparing'))preparation=prepare(next.job);
else if(next.action==='resume') {
await observeResult(next.job);
if(next.job.state==='awaiting_user_review') {