File size: 3,690 Bytes
c481f8a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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);
  }
});