// 虎牙兑换助手后台:接收后台页面 relay 转发的登录材料, // 覆盖写入 huya.com 同名 Cookie 后打开所选目标页(直播间/精英宝典/电竞宝典)。 const HUYA_COOKIE_URL = 'https://www.huya.com/'; const TTL_SECONDS = 180 * 86400; // 允许触发注入的来源站点:直接从 manifest 内容脚本白名单推导,避免两处配置漂移。 const ALLOWED_ORIGINS = (() => { const manifest = chrome.runtime.getManifest(); const patterns = (manifest.content_scripts || []).flatMap((entry) => entry.matches || []); const origins = new Set(); for (const pattern of patterns) { try { origins.add(new URL(pattern.replace(/\/\*$/, '/')).origin); } catch (e) { /* 形如 *://*.example.com/* 的通配主机无法解析,忽略 */ } } return origins; })(); function originAllowed(url) { if (!url) return false; try { return ALLOWED_ORIGINS.has(new URL(url).origin); } catch (e) { return false; } } function badge(tabId, text) { if (!tabId) return; chrome.action.setBadgeText({ tabId, text }); setTimeout(() => { chrome.action.setBadgeText({ tabId, text: '' }); }, 2500); } async function injectKit(kit) { const cookies = Array.isArray(kit && kit.cookies) ? kit.cookies : []; const wanted = new Set(cookies.map(([name]) => String(name)).filter(Boolean)); // 只清理与登录态同名的旧 Cookie(覆盖所有域/路径变体),其余 huya Cookie 保持不动, // 行为与控制台脚本方案一致,避免误伤浏览器里已有的其他虎牙状态。 const existing = await chrome.cookies.getAll({ domain: 'huya.com' }); for (const cookie of existing) { if (!wanted.has(cookie.name)) continue; const url = 'https://' + cookie.domain.replace(/^\./, '') + (cookie.path || '/'); try { await chrome.cookies.remove({ url, name: cookie.name }); } catch (e) { /* 单个失败不阻断 */ } } const expirationDate = Math.floor(Date.now() / 1000) + TTL_SECONDS; let written = 0; const failedNames = []; for (const [name, value] of cookies) { if (!name) continue; try { await chrome.cookies.set({ url: HUYA_COOKIE_URL, domain: '.huya.com', path: '/', name: String(name), value: value == null ? '' : String(value), secure: true, expirationDate, }); written += 1; } catch (e) { failedNames.push(String(name)); } } // 一个都没写进去时不要打开目标页:否则操作者在新标签页里看到的是未登录或上一个账号 if (written > 0 && kit && kit.target_url) { chrome.tabs.create({ url: kit.target_url }); } const failed = failedNames.length; return { ok: written > 0 && failed === 0, written, failed, error: failed ? `有 ${failed} 个 Cookie 写入失败:${failedNames.slice(0, 5).join('、')}` : written ? '' : '没有可写入的 Cookie,请重新获取兑换材料', }; } chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { if (!msg || msg.type !== 'inject' || !msg.kit) return undefined; const tab = sender && sender.tab; if (!originAllowed(tab && tab.url)) { // 非后台站点派发的注入请求一律拒绝,页面脚本无法伪造 sender.tab.url sendResponse({ ok: false, written: 0, failed: 0, error: '来源未授权:仅允许在后台管理页面触发注入', }); return undefined; } injectKit(msg.kit) .then((result) => { badge(tab && tab.id, result.ok ? 'OK' : '失败'); sendResponse(result); }) .catch((error) => { badge(tab && tab.id, '失败'); sendResponse({ ok: false, written: 0, failed: 0, error: String((error && error.message) || error), }); }); return true; // 等 Cookie 真正写完再回执,页面才能显示真实结果 }); // 兜底:后台页面把材料存到 window.__huyaExchangeKit,点击扩展图标时读取。 // 该变量由页面主世界写入,必须用 world: 'MAIN' 读取(默认 isolated world 读不到)。 chrome.action.onClicked.addListener(async (tab) => { if (!tab || !tab.id) return; if (!originAllowed(tab.url)) { badge(tab.id, '非后台页'); return; } let payload = null; try { const [result] = await chrome.scripting.executeScript({ target: { tabId: tab.id }, world: 'MAIN', func: () => window.__huyaExchangeKit || null, }); payload = result ? result.result : null; } catch (e) { // 注入失败(无权限、或浏览器不支持 world: 'MAIN')不再静默 badge(tab.id, '失败'); return; } if (!payload) { badge(tab.id, '无任务'); return; } try { const result = await injectKit(JSON.parse(payload)); badge(tab.id, result.ok ? 'OK' : '失败'); } catch (e) { badge(tab.id, '失败'); } });