2026-06-21 12:50:31 +00:00
|
|
|
/** @typedef {{ name: string; value: string; domain: string; path: string; expires: number; httpOnly: boolean; secure: boolean; sameSite: string }} PlaywrightCookie */
|
|
|
|
|
|
2026-06-23 16:55:10 +00:00
|
|
|
const COOKIE_DOMAINS = [
|
|
|
|
|
"threads.com",
|
|
|
|
|
"threads.net",
|
|
|
|
|
"instagram.com",
|
|
|
|
|
"facebook.com",
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const THREADS_TAB_URLS = [
|
|
|
|
|
"https://www.threads.com/*",
|
|
|
|
|
"https://threads.com/*",
|
|
|
|
|
"https://www.threads.net/*",
|
|
|
|
|
"https://threads.net/*",
|
|
|
|
|
];
|
2026-06-21 12:50:31 +00:00
|
|
|
|
|
|
|
|
function mapSameSite(sameSite) {
|
|
|
|
|
if (sameSite === "no_restriction") return "None";
|
|
|
|
|
if (sameSite === "lax") return "Lax";
|
|
|
|
|
if (sameSite === "strict") return "Strict";
|
|
|
|
|
return "Lax";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @param {chrome.cookies.Cookie} cookie */
|
|
|
|
|
function toPlaywrightCookie(cookie) {
|
|
|
|
|
return {
|
|
|
|
|
name: cookie.name,
|
|
|
|
|
value: cookie.value,
|
|
|
|
|
domain: cookie.domain,
|
|
|
|
|
path: cookie.path || "/",
|
|
|
|
|
expires: cookie.expirationDate ?? -1,
|
|
|
|
|
httpOnly: cookie.httpOnly ?? false,
|
|
|
|
|
secure: cookie.secure ?? false,
|
|
|
|
|
sameSite: mapSameSite(cookie.sameSite),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function collectThreadsCookies() {
|
|
|
|
|
/** @type {PlaywrightCookie[]} */
|
|
|
|
|
const merged = [];
|
|
|
|
|
const seen = new Set();
|
|
|
|
|
|
|
|
|
|
for (const domain of COOKIE_DOMAINS) {
|
|
|
|
|
const batch = await chrome.cookies.getAll({ domain });
|
|
|
|
|
for (const cookie of batch) {
|
|
|
|
|
const key = `${cookie.domain}|${cookie.path}|${cookie.name}`;
|
|
|
|
|
if (seen.has(key)) continue;
|
|
|
|
|
seen.add(key);
|
|
|
|
|
merged.push(toPlaywrightCookie(cookie));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-23 16:55:10 +00:00
|
|
|
// Some Chrome builds store Meta login cookies only on exact host URLs.
|
|
|
|
|
const urls = [
|
|
|
|
|
"https://www.threads.com/",
|
|
|
|
|
"https://www.threads.net/",
|
|
|
|
|
"https://www.instagram.com/",
|
|
|
|
|
];
|
|
|
|
|
for (const url of urls) {
|
|
|
|
|
const batch = await chrome.cookies.getAll({ url });
|
|
|
|
|
for (const cookie of batch) {
|
|
|
|
|
const key = `${cookie.domain}|${cookie.path}|${cookie.name}`;
|
|
|
|
|
if (seen.has(key)) continue;
|
|
|
|
|
seen.add(key);
|
|
|
|
|
merged.push(toPlaywrightCookie(cookie));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 12:50:31 +00:00
|
|
|
return merged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function collectThreadsLocalStorage() {
|
2026-06-23 16:55:10 +00:00
|
|
|
const tabs = await chrome.tabs.query({ url: THREADS_TAB_URLS });
|
2026-06-21 12:50:31 +00:00
|
|
|
|
|
|
|
|
if (!tabs.length || tabs[0].id == null) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const [injection] = await chrome.scripting.executeScript({
|
|
|
|
|
target: { tabId: tabs[0].id },
|
|
|
|
|
func: () => {
|
|
|
|
|
const localStorageItems = [];
|
|
|
|
|
for (let i = 0; i < localStorage.length; i += 1) {
|
|
|
|
|
const name = localStorage.key(i);
|
|
|
|
|
if (!name) continue;
|
|
|
|
|
localStorageItems.push({ name, value: localStorage.getItem(name) ?? "" });
|
|
|
|
|
}
|
|
|
|
|
return { origin: location.origin, localStorage: localStorageItems };
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const payload = injection?.result;
|
|
|
|
|
if (!payload || !Array.isArray(payload.localStorage)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [payload];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function buildStorageState() {
|
|
|
|
|
const cookies = await collectThreadsCookies();
|
|
|
|
|
const origins = await collectThreadsLocalStorage();
|
|
|
|
|
|
|
|
|
|
if (!cookies.length) {
|
2026-06-23 16:55:10 +00:00
|
|
|
throw new Error(
|
|
|
|
|
"找不到 Threads / Instagram cookies。請先在 Chrome 開啟 threads.com 或 threads.net 並完成登入"
|
|
|
|
|
);
|
2026-06-21 12:50:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return JSON.stringify({ cookies, origins });
|
|
|
|
|
}
|