⚡ 常用设置
自动作答
📝 文本 AI(普通题目)
🎧 音频 AI(听力题)
⭐ 推荐千问 · 基本满分
智谱限流严,备选
`;
document.body.appendChild(panel);
this.panelEl = panel;
this.menuSelectEl = panel.querySelector('#up-menu-select');
this.timeInputEl = panel.querySelector('#up-time-input');
this.startBtn = panel.querySelector('#up-start-btn');
this.pauseBtn = panel.querySelector('#up-pause-btn');
this.stopBtn = panel.querySelector('#up-stop-btn');
this.logEl = panel.querySelector('#unipus-log');
// 恢复输入
this.timeInputEl.value = Store.getLastTimeValue();
// 事件
panel.querySelector('#up-close-btn').onclick = () => {
panel.style.display = 'none';
Store.setPanelOpen(false);
};
// 最小化:收回悬浮球
panel.querySelector('#up-min-btn').onclick = () => {
panel.style.display = 'none';
};
// 拖动面板
(() => {
const header = panel.querySelector('.up-header');
let dragging = false, ox = 0, oy = 0;
header.addEventListener('mousedown', (e) => {
if (e.target.closest('.up-iconbtn')) return; // 不拦截按钮点击
dragging = true;
ox = e.clientX - panel.getBoundingClientRect().left;
oy = e.clientY - panel.getBoundingClientRect().top;
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!dragging) return;
let x = e.clientX - ox;
let y = e.clientY - oy;
// 限制不超出视口
x = Math.max(0, Math.min(window.innerWidth - panel.offsetWidth, x));
y = Math.max(0, Math.min(window.innerHeight - 40, y));
panel.style.left = x + 'px';
panel.style.top = y + 'px';
panel.style.right = 'auto';
panel.style.bottom = 'auto';
});
document.addEventListener('mouseup', () => { dragging = false; });
})();
panel.querySelector('#up-settings-btn').onclick = () => this.openSettings();
panel.querySelector('#up-refresh-btn').onclick = () => {
this.addLog('🔄 重新扫描目录...');
_menuListCache = [];
window.__unipusDiagSent = false;
window.__unipusHeurLogged = false;
IframeRPC.requestScan();
};
panel.querySelector('#up-clear-log').onclick = () => {
if (this.logEl) this.logEl.innerHTML = '';
};
// 微信号复制
const wechatEl = panel.querySelector('#up-wechat-copy');
if (wechatEl) {
wechatEl.onclick = async () => {
const wechat = 'C919irt';
let ok = false;
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(wechat);
ok = true;
}
} catch (_) {}
if (!ok) {
// 回退到 execCommand
try {
const ta = document.createElement('textarea');
ta.value = wechat;
ta.style.cssText = 'position:fixed;opacity:0;pointer-events:none;';
document.body.appendChild(ta);
ta.select();
ok = document.execCommand('copy');
document.body.removeChild(ta);
} catch (_) {}
}
wechatEl.classList.add('copied');
wechatEl.textContent = ok ? '✅ 已复制 C919irt' : '❌ 复制失败,请手动';
setTimeout(() => {
wechatEl.classList.remove('copied');
wechatEl.textContent = 'C919irt 📋';
}, 1800);
};
}
// 常用设置自动保存
const autoAnswerEl = panel.querySelector('#up-autoAnswer');
autoAnswerEl.onchange = () => {
const c = Store.getFeatureConf();
Store.setFeatureConf({ ...c, autoAnswer: autoAnswerEl.checked });
this.addLog(`✅ 自动作答:${autoAnswerEl.checked ? '开' : '关'}`);
};
const aiProviderEl = panel.querySelector('#up-aiProvider');
const aiLinkEl = panel.querySelector('#up-ai-link');
const updateAILink = () => {
const conf = AI_PROVIDERS[aiProviderEl.value];
if (!conf) return;
aiLinkEl.href = conf.link;
aiLinkEl.textContent = `${conf.linkText} · ${conf.keyHint}`;
};
updateAILink();
aiProviderEl.onchange = () => {
Store.setAIProvider(aiProviderEl.value);
updateAILink();
this.addLog(`✅ AI 模型:${AI_PROVIDERS[aiProviderEl.value]?.name || aiProviderEl.value}`);
};
const aiKeyEl = panel.querySelector('#up-aiKey');
aiKeyEl.onblur = () => {
const v = aiKeyEl.value.trim();
if (v !== Store.getAIApiKey()) {
Store.setAIApiKey(v);
this.addLog('✅ 文本 AI Key 已保存');
}
};
panel.querySelector('#up-key-toggle').onclick = () => {
aiKeyEl.type = aiKeyEl.type === 'password' ? 'text' : 'password';
};
// ---- 音频 AI 控件 ----
const audioProviderEl = panel.querySelector('#up-audioProvider');
const audioLinkEl = panel.querySelector('#up-audio-link');
const updateAudioLink = () => {
const conf = AI_PROVIDERS[audioProviderEl.value];
if (!conf) return;
audioLinkEl.href = conf.link;
audioLinkEl.textContent = `${conf.linkText} · ${conf.keyHint}`;
};
updateAudioLink();
audioProviderEl.onchange = () => {
Store.setAudioProvider(audioProviderEl.value);
updateAudioLink();
this.addLog(`✅ 音频 AI:${AI_PROVIDERS[audioProviderEl.value]?.name || audioProviderEl.value}`);
};
const audioKeyEl = panel.querySelector('#up-audioKey');
audioKeyEl.onblur = () => {
const v = audioKeyEl.value.trim();
if (v !== Store.getAudioApiKey()) {
Store.setAudioApiKey(v);
this.addLog('✅ 音频 AI Key 已保存');
}
};
panel.querySelector('#up-audio-key-toggle').onclick = () => {
audioKeyEl.type = audioKeyEl.type === 'password' ? 'text' : 'password';
};
// ---- 广告模式 / 解锁码 ----
const unlockInput = panel.querySelector('#up-unlock-input');
const unlockBtn = panel.querySelector('#up-unlock-btn');
unlockBtn.onclick = async () => {
const code = unlockInput.value.trim();
if (!/^\d{4}$/.test(code)) {
this.addLog('⚠️ 请输入4位数字解锁码');
unlockInput.focus();
return;
}
unlockBtn.disabled = true;
unlockBtn.textContent = '验证中...';
try {
const r = await Solver.verifyUnlockCode(code);
Store.setVerifyValidUntil(r.validUntil, code, r.sessionToken || '', r.scope || 'ai-unlock');
unlockInput.value = '';
this.addLog(`✅ 解锁成功!有效期至 ${r.validUntilStr}`);
this.refreshUnlockUI();
} catch (err) {
this.addLog(`❌ 解锁失败:${err}`);
} finally {
unlockBtn.disabled = false;
unlockBtn.textContent = '🔓 解锁';
}
};
unlockInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') unlockBtn.click();
});
panel.querySelector('#up-unlock-clear').onclick = () => {
if (confirm('确定注销当前解锁?')) {
Store.clearVerifySession();
this.addLog('✅ 已注销解锁,恢复广告模式');
this.refreshUnlockUI();
}
};
this.refreshUnlockUI();
// 每秒刷新一次倒计时
if (this._unlockTimer) clearInterval(this._unlockTimer);
this._unlockTimer = setInterval(() => this.refreshUnlockUI(), 1000);
this.startBtn.onclick = () => this.onStart();
this.menuSelectEl.onchange = () => this.rememberMenuSelection();
this.pauseBtn.onclick = () => this.onPause();
this.stopBtn.onclick = () => this.onStop();
// 监听菜单就绪
window.addEventListener('UNIPUS_MENU_READY', (e) => {
this.populateMenu(e.detail);
});
if (_menuListCache.length) this.populateMenu(_menuListCache);
else this.populateMenu([]);
// 监听日志(去重防止 panel 重建时重复绑定)
if (!Panel._logBound) {
Panel._logBound = true;
IframeRPC.onLog((msg) => this.addLog(msg));
}
// 远程公告
this._fetchAnnounce(panel);
},
_fetchAnnounce(panel) {
if (typeof GM_xmlhttpRequest !== 'function') return;
try {
GM_xmlhttpRequest({
method: 'GET',
url: Config.announceUrl,
timeout: 8000,
onload: (resp) => {
try {
const res = JSON.parse(resp.responseText);
if (res.code !== 1 || !res.data || !res.data.enabled) return;
this._renderAnnounce(panel, res.data);
} catch (_) {}
},
onerror: () => {}, ontimeout: () => {},
});
} catch (_) {}
},
_renderAnnounce(panel, data) {
const root = panel.querySelector('#up-announce');
const titleEl = panel.querySelector('#up-announce-title');
const contentEl = panel.querySelector('#up-announce-content');
const closeEl = panel.querySelector('#up-announce-close');
const iconEl = root && root.querySelector('.up-announce-icon');
if (!root || !titleEl || !contentEl) return;
const type = ['info','warning','error','success'].includes(data.type) ? data.type : 'info';
const icon = { info: '📢', warning: '⚠️', error: '❌', success: '✅' }[type];
if (iconEl) iconEl.textContent = icon;
titleEl.textContent = data.title || '公告';
let rawContent = String(data.content || '');
let noticeImages = [];
if (Array.isArray(data.images)) {
noticeImages = data.images.slice();
} else if (typeof data.images === 'string' && data.images.trim()) {
try {
const parsed = JSON.parse(data.images);
noticeImages = Array.isArray(parsed) ? parsed : [data.images];
} catch (_) {
noticeImages = data.images.split(/[\n,]+/);
}
}
if (data.image_url) noticeImages.push(data.image_url);
rawContent = rawContent.replace(/!\[[^\]]*\]\(([^)]+)\)/g, (_, url) => {
noticeImages.push(url);
return '';
}).trim();
const normalizeImageUrl = (url) => {
url = String(url || '').trim();
if (!url) return '';
if (/^https?:\/\//i.test(url) || /^data:image\//i.test(url)) return url;
if (/^\/\//.test(url)) return 'https:' + url;
try { return new URL(url, 'https://qsy.iano.cn/').href; } catch (_) {}
return '';
};
noticeImages = noticeImages.map(normalizeImageUrl).filter((url, idx, arr) => url && arr.indexOf(url) === idx).slice(0, 6);
// 简易白名单 sanitizer
const tmp = document.createElement('div');
tmp.innerHTML = rawContent;
tmp.querySelectorAll('*').forEach(el => {
const tag = el.tagName.toLowerCase();
if (!['br','b','i','u','strong','em','span'].includes(tag)) {
el.replaceWith(document.createTextNode(el.textContent || ''));
return;
}
[...el.attributes].forEach(attr => {
if (/^on/i.test(attr.name) || attr.name === 'style') el.removeAttribute(attr.name);
});
});
contentEl.innerHTML = tmp.innerHTML;
if (noticeImages.length) {
const imageBox = document.createElement('div');
imageBox.className = 'up-announce-images';
noticeImages.forEach(url => {
const img = document.createElement('img');
img.src = url;
img.alt = '公告图片';
img.loading = 'lazy';
img.referrerPolicy = 'no-referrer';
img.addEventListener('click', () => {
try { window.open(url, '_blank', 'noopener,noreferrer'); } catch (_) {}
});
imageBox.appendChild(img);
});
contentEl.appendChild(imageBox);
}
root.classList.remove('info','warning','error','success');
root.classList.add(type, 'show');
if (closeEl) closeEl.onclick = () => { root.classList.remove('show'); };
},
_purposeResolver: null,
_getPurposeOverlay() {
this.injectStyles();
let overlay = document.getElementById('unipus-purpose-overlay');
if (overlay) return overlay;
overlay = document.createElement('div');
overlay.id = 'unipus-purpose-overlay';
overlay.setAttribute('data-unipus-helper', '1');
overlay.innerHTML = `