335 lines
8.0 KiB
JavaScript
335 lines
8.0 KiB
JavaScript
/**
|
||
* 帅库之家Cookie管理 - 后台服务
|
||
*/
|
||
|
||
// 导入MD5工具
|
||
importScripts('utils/md5.js');
|
||
|
||
// 平台配置
|
||
const PLATFORMS = {
|
||
'commerce': {
|
||
name: '巨量百应-达人账号',
|
||
urlPattern: /buyin\.jinritemai\.com/,
|
||
cookieUrl: 'https://buyin.jinritemai.com', // 用于获取Cookie的URL
|
||
loginUrl: 'https://buyin.jinritemai.com/mpa/account/institution-role-select'
|
||
},
|
||
'entertainment': {
|
||
name: '字节联盟-主播排行',
|
||
urlPattern: /union\.bytedance\.com/,
|
||
cookieUrl: 'https://union.bytedance.com', // 用于获取Cookie的URL
|
||
loginUrl: 'https://union.bytedance.com/open/portal/data/leaderboard?appId=3000'
|
||
}
|
||
};
|
||
|
||
// 默认配置
|
||
const DEFAULT_CONFIG = {
|
||
serverUrl: 'https://api.skzhijia.com',
|
||
apiKey: '',
|
||
pollInterval: 5 // 分钟
|
||
};
|
||
|
||
// 状态缓存
|
||
let statusCache = {};
|
||
|
||
/**
|
||
* 初始化
|
||
*/
|
||
chrome.runtime.onInstalled.addListener(async () => {
|
||
console.log('帅库之家Cookie管理插件已安装');
|
||
|
||
// 初始化存储
|
||
const config = await getConfig();
|
||
if (!config.serverUrl) {
|
||
await chrome.storage.sync.set({ config: DEFAULT_CONFIG });
|
||
}
|
||
|
||
// 设置定时轮询
|
||
await setupAlarm();
|
||
|
||
// 初始化图标
|
||
await updateIcon('gray');
|
||
});
|
||
|
||
/**
|
||
* 监听定时器
|
||
*/
|
||
chrome.alarms.onAlarm.addListener(async (alarm) => {
|
||
if (alarm.name === 'pollStatus') {
|
||
await pollCookieStatus();
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 设置定时轮询
|
||
*/
|
||
async function setupAlarm() {
|
||
const config = await getConfig();
|
||
const interval = config.pollInterval || 5;
|
||
|
||
// 清除现有定时器
|
||
await chrome.alarms.clear('pollStatus');
|
||
|
||
// 设置新定时器
|
||
chrome.alarms.create('pollStatus', {
|
||
periodInMinutes: interval
|
||
});
|
||
|
||
console.log(`定时轮询已设置: 每${interval}分钟`);
|
||
}
|
||
|
||
/**
|
||
* 轮询Cookie状态
|
||
*/
|
||
async function pollCookieStatus() {
|
||
const config = await getConfig();
|
||
|
||
if (!config.serverUrl || !config.apiKey) {
|
||
console.log('未配置服务器地址或API Key');
|
||
await updateIcon('gray');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(`${config.serverUrl}/api/crawler/cookie/status`, {
|
||
method: 'GET',
|
||
headers: {
|
||
'X-API-Key': config.apiKey,
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP ${response.status}`);
|
||
}
|
||
|
||
const data = await response.json();
|
||
if (data.code === 200 && data.data) {
|
||
statusCache = {};
|
||
let hasExpired = false;
|
||
|
||
for (const item of data.data) {
|
||
statusCache[item.code] = item;
|
||
if (!item.isValid || item.lastRunStatus === 3) {
|
||
hasExpired = true;
|
||
}
|
||
}
|
||
|
||
// 更新图标
|
||
await updateIcon(hasExpired ? 'red' : 'green');
|
||
|
||
// 存储状态
|
||
await chrome.storage.local.set({ statusCache });
|
||
}
|
||
} catch (error) {
|
||
console.error('轮询Cookie状态失败:', error);
|
||
await updateIcon('red');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取指定平台的Cookie字符串
|
||
* 使用url参数获取该URL能访问到的所有Cookie(与浏览器实际发送请求时一致)
|
||
*/
|
||
async function getCookieStringForPlatform(code) {
|
||
const platform = PLATFORMS[code];
|
||
if (!platform) {
|
||
throw new Error('未知的平台代码');
|
||
}
|
||
|
||
return new Promise((resolve, reject) => {
|
||
// 使用url参数而不是domain,这样能获取到所有该URL可访问的Cookie
|
||
chrome.cookies.getAll({ url: platform.cookieUrl }, (cookies) => {
|
||
if (!cookies || cookies.length === 0) {
|
||
reject(new Error('未找到Cookie,请先登录对应平台'));
|
||
return;
|
||
}
|
||
console.log("获取到的Cookie数量:", cookies.length);
|
||
// 直接拼接成 name=value; name2=value2 格式
|
||
const cookieString = cookies.map(c => `${c.name}=${c.value}`).join('; ');
|
||
resolve(cookieString);
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 上传Cookie
|
||
*/
|
||
async function uploadCookie(code, manualCookieString = null) {
|
||
const config = await getConfig();
|
||
const platform = PLATFORMS[code];
|
||
|
||
if (!platform) {
|
||
throw new Error('未知的平台代码');
|
||
}
|
||
|
||
if (!config.serverUrl || !config.apiKey) {
|
||
throw new Error('请先配置服务器地址和API Key');
|
||
}
|
||
|
||
// 获取Cookie字符串 (使用手动输入的或从浏览器获取)
|
||
let cookieString;
|
||
if (manualCookieString) {
|
||
cookieString = manualCookieString.trim();
|
||
if (!cookieString) {
|
||
throw new Error('Cookie字符串不能为空');
|
||
}
|
||
} else {
|
||
cookieString = await getCookieStringForPlatform(code);
|
||
}
|
||
|
||
const md5Hash = await computeMD5(cookieString);
|
||
|
||
// 上传
|
||
const response = await fetch(`${config.serverUrl}/api/crawler/cookie/upload`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-API-Key': config.apiKey,
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
code: code,
|
||
cookieString: cookieString,
|
||
md5: md5Hash,
|
||
url: platform.loginUrl
|
||
})
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const error = await response.text();
|
||
throw new Error(`上传失败: ${error}`);
|
||
}
|
||
|
||
const result = await response.json();
|
||
if (result.code !== 200) {
|
||
throw new Error(result.msg || '上传失败');
|
||
}
|
||
|
||
// 刷新状态
|
||
await pollCookieStatus();
|
||
|
||
return result.data;
|
||
}
|
||
|
||
/**
|
||
* 计算MD5
|
||
*/
|
||
async function computeMD5(text) {
|
||
// 使用导入的md5函数
|
||
return md5(text);
|
||
}
|
||
|
||
/**
|
||
* 更新图标
|
||
*/
|
||
async function updateIcon(status) {
|
||
const icons = {
|
||
'green': {
|
||
'16': 'icons/icon-green-16.png',
|
||
'32': 'icons/icon-green-32.png',
|
||
'48': 'icons/icon-green-48.png',
|
||
'128': 'icons/icon-green-128.png'
|
||
},
|
||
'red': {
|
||
'16': 'icons/icon-red-16.png',
|
||
'32': 'icons/icon-red-32.png',
|
||
'48': 'icons/icon-red-48.png',
|
||
'128': 'icons/icon-red-128.png'
|
||
},
|
||
'gray': {
|
||
'16': 'icons/icon-gray-16.png',
|
||
'32': 'icons/icon-gray-32.png',
|
||
'48': 'icons/icon-gray-48.png',
|
||
'128': 'icons/icon-gray-128.png'
|
||
}
|
||
};
|
||
|
||
try {
|
||
await chrome.action.setIcon({ path: icons[status] || icons['gray'] });
|
||
} catch (error) {
|
||
console.error('更新图标失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取配置
|
||
*/
|
||
async function getConfig() {
|
||
const result = await chrome.storage.sync.get('config');
|
||
return result.config || DEFAULT_CONFIG;
|
||
}
|
||
|
||
/**
|
||
* 保存配置
|
||
*/
|
||
async function saveConfig(config) {
|
||
await chrome.storage.sync.set({ config });
|
||
await setupAlarm();
|
||
}
|
||
|
||
/**
|
||
* 获取状态缓存
|
||
*/
|
||
async function getStatusCache() {
|
||
const result = await chrome.storage.local.get('statusCache');
|
||
return result.statusCache || statusCache;
|
||
}
|
||
|
||
/**
|
||
* 消息处理
|
||
*/
|
||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||
(async () => {
|
||
try {
|
||
switch (message.action) {
|
||
case 'getConfig':
|
||
sendResponse({ success: true, data: await getConfig() });
|
||
break;
|
||
|
||
case 'saveConfig':
|
||
await saveConfig(message.config);
|
||
sendResponse({ success: true });
|
||
break;
|
||
|
||
case 'getStatus':
|
||
sendResponse({ success: true, data: await getStatusCache() });
|
||
break;
|
||
|
||
case 'pollStatus':
|
||
await pollCookieStatus();
|
||
sendResponse({ success: true, data: await getStatusCache() });
|
||
break;
|
||
|
||
case 'uploadCookie':
|
||
// 支持手动上传Cookie字符串
|
||
const result = await uploadCookie(message.code, message.cookieString);
|
||
sendResponse({ success: true, data: result });
|
||
break;
|
||
|
||
case 'getCookieString':
|
||
// 获取指定平台的Cookie字符串
|
||
const cookieStr = await getCookieStringForPlatform(message.code);
|
||
sendResponse({ success: true, data: cookieStr });
|
||
break;
|
||
|
||
case 'openLogin':
|
||
const platform = PLATFORMS[message.code];
|
||
if (platform) {
|
||
chrome.tabs.create({ url: platform.loginUrl });
|
||
}
|
||
sendResponse({ success: true });
|
||
break;
|
||
|
||
default:
|
||
sendResponse({ success: false, error: '未知操作' });
|
||
}
|
||
} catch (error) {
|
||
sendResponse({ success: false, error: error.message });
|
||
}
|
||
})();
|
||
|
||
return true; // 保持消息通道开放
|
||
});
|
||
|
||
// 启动时执行一次轮询
|
||
pollCookieStatus();
|