| const getActiveTab = () => |
| new Promise((resolve) => { |
| chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => resolve(tabs && tabs[0] ? tabs[0] : null)); |
| }); |
|
|
| const storageGet = (keys) => |
| new Promise((resolve) => { |
| chrome.storage.local.get(keys, (items) => resolve(items || {})); |
| }); |
|
|
| const storageSet = (items) => |
| new Promise((resolve) => { |
| chrome.storage.local.set(items, () => resolve()); |
| }); |
|
|
| const sendToTab = (tabId, message) => |
| new Promise((resolve, reject) => { |
| chrome.tabs.sendMessage(tabId, message, (resp) => { |
| const err = chrome.runtime.lastError; |
| if (err) { |
| reject(new Error(err.message)); |
| return; |
| } |
| resolve(resp); |
| }); |
| }); |
|
|
| const badge = async (text) => { |
| try { |
| await chrome.action.setBadgeBackgroundColor({ color: "#2D7D46" }); |
| await chrome.action.setBadgeText({ text: text || "" }); |
| } catch (e) {} |
| }; |
|
|
| const badgeError = async (text) => { |
| try { |
| await chrome.action.setBadgeBackgroundColor({ color: "#B42318" }); |
| await chrome.action.setBadgeText({ text: text || "" }); |
| } catch (e) {} |
| }; |
|
|
| const normalizeBaseUrl = (url) => { |
| const u = String(url || "").trim(); |
| if (!u) return ""; |
| return u.endsWith("/") ? u.slice(0, -1) : u; |
| }; |
|
|
| const extractTaskId = (text) => { |
| const t = String(text || "").trim(); |
| if (!t) return ""; |
| const m = t.match(/[0-9a-fA-F]{32}/); |
| return m ? m[0] : t; |
| }; |
|
|
| const getOrPromptServiceUrl = async (tabId) => { |
| const items = await storageGet(["service_url"]); |
| const existing = normalizeBaseUrl(items.service_url); |
| if (existing) return existing; |
| const resp = await sendToTab(tabId, { |
| type: "PROMPT", |
| text: "Spider_XHS 服务地址(例如 http://localhost:8000)", |
| defaultValue: "http://localhost:8000", |
| }); |
| const v = normalizeBaseUrl(resp && resp.value); |
| if (v) await storageSet({ service_url: v }); |
| return v; |
| }; |
|
|
| const getTaskId = async (tabId) => { |
| let clip = ""; |
| try { |
| const resp = await sendToTab(tabId, { type: "READ_CLIPBOARD" }); |
| clip = extractTaskId(resp && resp.text); |
| } catch (e) {} |
| if (clip) return clip; |
| const resp = await sendToTab(tabId, { type: "PROMPT", text: "task_id", defaultValue: "" }); |
| return extractTaskId(resp && resp.value); |
| }; |
|
|
| const collectPage = async (tabId) => { |
| const resp = await sendToTab(tabId, { type: "COLLECT_PAGE" }); |
| return resp || {}; |
| }; |
|
|
| chrome.action.onClicked.addListener(async () => { |
| await badge("..."); |
| try { |
| const tab = await getActiveTab(); |
| const tabId = tab && tab.id; |
| if (!tabId) { |
| await badgeError("NO"); |
| return; |
| } |
|
|
| const serviceUrl = await getOrPromptServiceUrl(tabId); |
| if (!serviceUrl) { |
| await badgeError("URL"); |
| return; |
| } |
|
|
| const taskId = await getTaskId(tabId); |
| if (!taskId) { |
| await badgeError("ID"); |
| return; |
| } |
|
|
| const page = await collectPage(tabId); |
| const url = String(page.url || "").trim(); |
| const html = String(page.html || ""); |
|
|
| const body = { |
| task_id: taskId, |
| raw: { url, html }, |
| normalized: { url, kind: "page" }, |
| meta: { source_engine: "extension_rpa", source_type: "page", source_ref: url, ingested_at: new Date().toISOString(), ok: true }, |
| }; |
|
|
| const endpoint = `${serviceUrl}/api/v1/import/extension`; |
| const resp = await fetch(endpoint, { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify(body), |
| }); |
|
|
| if (resp.ok) { |
| await badge("OK"); |
| } else { |
| await badgeError(String(resp.status || "ERR")); |
| } |
| } catch (e) { |
| await badgeError("ERR"); |
| } finally { |
| setTimeout(() => badge(""), 2000); |
| } |
| }); |
|
|
|
|