let tempMessageCounter = 1;
const throttledScan = throttle(() => {
console.log("⚡ 节流触发:刷新好友列表扫描");
scanUnreadList();
}, 2000);
function throttle(func, delay) {
let lastCall = 0;
let timeoutId;
return function (...args) {
const now = Date.now();
const remaining = delay - (now - lastCall);
if (remaining <= 0) {
lastCall = now;
func.apply(this, args);
} else {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
lastCall = Date.now();
func.apply(this, args);
}, remaining);
}
};
}
function observeFriendListContainer() {
const chatList = document.querySelector('.chat_grp_lst');
if (!chatList) {
console.warn("⚠️ 未找到好友列表父级容器,无法监听");
return;
}
const observer = new MutationObserver(() => {
throttledScan();
});
observer.observe(chatList, {
childList: true,
subtree: true,
attributes: true
});
console.log("✅ 已启动好友列表父级容器监听");
}
function injectFloatingUI() {
fetch(chrome.runtime.getURL('floating-ui.html')).then(res => res.text()).then(html => {
const div = document.createElement('div');
div.innerHTML = html;
document.body.appendChild(div);
const css1 = document.createElement('link');
css1.rel = 'stylesheet';
css1.href = chrome.runtime.getURL('floating-ui.css');
document.head.appendChild(css1);
const css2 = document.createElement('link');
css2.rel = 'stylesheet';
css2.href = chrome.runtime.getURL('style.css');
document.head.appendChild(css2);
import(chrome.runtime.getURL('floating-ui.js')).then(module => {
window.renderFloatingUI = module.renderUI;
chrome.storage.local.get("messages", res => {
window.renderFloatingUI(res.messages || {});
});
});
});
}
injectFloatingUI();
function extractContactFromItem(item) {
const chatId = item.getAttribute('data-key') || 'unknown';
const nameEl = item.querySelector("strong.name");
const name = nameEl?.innerText.trim() || '未知';
const groupCountEl = item.querySelector('span.name_area em.num');
if (groupCountEl) {
console.log(`�� 跳过群聊:${name}`);
return null;
}
const unreadEl = item.querySelector("span.new");
const unreadCount = unreadEl ? parseInt(unreadEl.innerText.trim()) : 0;
const timeEl = item.querySelector("em.date");
const msgTime = timeEl ? timeEl.innerText.trim() : '';
return {
chatId,
name,
avatar: item.querySelector(".thmb img")?.src || '',
unreadCount,
msgTime
};
}
function processSingleContact(item) {
const chatId = item.dataset.key;
const name = item.querySelector('.name')?.innerText?.trim() || '';
const avatar = item.querySelector('img')?.src || '';
if (!chatId) return;
chrome.storage.local.get("messages", res => {
const store = res.messages || {};
if (!store[chatId]) {
store[chatId] = {
name,
avatar,
messages: []
};
}
const contact = store[chatId];
const lastMsgText = item.querySelector("dd.msg")?.innerText?.trim();
const msgTime = item.querySelector("em.date")?.innerText?.trim() || new Date().toLocaleString();
const unreadEl = item.querySelector("span.new");
const unreadCount = unreadEl ? parseInt(unreadEl.innerText.trim()) : 0;
if (unreadCount > 0 && lastMsgText) {
const exists = contact.messages.some(m =>
m.text === lastMsgText && m.time === msgTime
);
if (!exists) {
const maxId = contact.messages.length
? contact.messages[contact.messages.length - 1].id
: 0;
contact.messages.push({
id: maxId + 1,
text: lastMsgText,
time: msgTime,
from: "friend",
isRead: false
});
console.log(`�� 新未读消息已写入(${chatId}):${lastMsgText}`);
} else {
console.log(`�� 未读消息重复,跳过(${chatId}):${lastMsgText}`);
}
} else {
console.log(`ℹ️ 已读状态或无新消息,跳过:${chatId}`);
}
contact.name = name;
contact.avatar = avatar;
chrome.storage.local.set({ messages: store });
});
}
function scanUnreadList() {
const items = document.querySelectorAll("li.item_chat[data-role='channel-item']");
console.log(`�� 正在扫描联系人,共 ${items.length} 个`);
chrome.storage.local.get("messages", (res) => {
const store = res.messages || {};
items.forEach(item => {
const chatId = item.getAttribute('data-key');
const name = item.querySelector('.name')?.innerText?.trim() || '';
const avatar = item.querySelector('img')?.src || '';
const lastMsgText = item.querySelector("dd.msg")?.innerText?.trim();
const msgTime = item.querySelector("em.date")?.innerText?.trim() || new Date().toLocaleString();
const unreadEl = item.querySelector("span.new");
if (!chatId || !lastMsgText) return;
if (!store[chatId]) {
store[chatId] = {
name,
avatar,
messages: []
};
}
const contact = store[chatId];
if (unreadEl) {
const exists = contact.messages.some(m =>
m.text === lastMsgText && m.time === msgTime
);
if (!exists) {
const maxId = contact.messages.length
? contact.messages[contact.messages.length - 1].id
: 0;
contact.messages.push({
id: maxId + 1,
text: lastMsgText,
time: msgTime,
from: "friend",
isRead: false
});
console.log(`�� [刷新] 新未读消息写入(${chatId}):${lastMsgText}`);
} else {
console.log(`�� [刷新] 未读消息重复,跳过(${chatId}):${lastMsgText}`);
}
} else {
console.log(`ℹ️ [刷新] 已读消息跳过:${chatId}`);
}
contact.name = name;
contact.avatar = avatar;
});
chrome.storage.local.set({ messages: store }, () => {
renderFloatingUI(store);
});
});
console.log(`✅ 全量刷新已完成`);
}
function observeEachContact() {
const contacts = document.querySelectorAll('li.item_chat');
contacts.forEach(item => {
const observer = new MutationObserver(() => {
console.log('�� 单个联系人DOM变化:', item);
processSingleContact(item);
});
observer.observe(item, {
childList: true,
subtree: true,
characterData: true,
attributes: true
});
});
console.log(`✅ 已为 ${contacts.length} 个联系人设置单独监听`);
}
function getCurrentChatId() {
const selectedItem = document.querySelector('li.item_chat.selected[data-key]');
const idValue = selectedItem ? selectedItem.getAttribute('data-key') : null;
if (!idValue) {
console.warn("⚠️ 未找到已选中的联系人或 data-key 属性为空(请确认页面停留在某个联系人聊天室内)");
return null;
}
console.log(`�� 当前页面ID(来自联系人列表 selected 项)=[${idValue}]`);
return idValue;
}
function getChatHeaderInfo() {
const name = document.querySelector('.info_box span.name')?.innerText.trim() || '未知';
const avatar = document.querySelector('.info_box img')?.src || '';
return { name, avatar };
}
function extractHistoryMessage(item) {
if (item.classList.contains('inform_date')) {
const dateText = item.querySelector('span.date')?.innerText.trim() || '';
return {
type: 'date',
text: dateText
};
}
if (item.classList.contains('msg_wrap')) {
const text = item.querySelector('.msg_box .msg')?.innerText.trim() || '';
const time = item.querySelector('.status_box .date')?.innerText.trim() || '';
const readTag = item.querySelector('.read_state')?.innerText?.trim() || '';
const fromSelf = item.classList.contains('me');
return {
type: 'message',
text,
time,
readState: readTag,
isFromMe: fromSelf
};
}
return null;
}
function collectHistoryMessagesInChat() {
const chatId = getCurrentChatId();
if (!chatId) {
console.warn("[content.js] ⚠️ 当前不在有效聊天室内,无法采集历史记录");
return;
}
const headerInfo = getChatHeaderInfo();
const items = document.querySelectorAll('.chat_view .msg_wrap, .chat_view .inform_date');
if (!items.length) {
console.warn("[content.js] ⚠️ 页面内未找到任何消息节点");
return;
}
console.log(`[content.js] �� 从页面采集到 ${items.length} 条消息`);
const newMessages = [];
items.forEach(item => {
const msg = extractHistoryMessage(item);
if (msg && msg.text) {
newMessages.push(msg);
}
});
chrome.storage.local.get("messages", res => {
const store = res.messages || {};
if (!store[chatId]) {
store[chatId] = {
name: headerInfo.name,
avatar: headerInfo.avatar,
messages: []
};
}
const chat = store[chatId];
chat.name = headerInfo.name;
chat.avatar = headerInfo.avatar;
const msgMap = {};
chat.messages.forEach(msg => {
msgMap[msg.id] = msg;
});
newMessages.forEach(msg => {
msgMap[msg.id] = msg;
});
chat.messages = Object.values(msgMap).sort((a, b) => a.id - b.id);
chrome.storage.local.set({ messages: store }, () => {
console.log(`[content.js] ✅ ${chat.messages.length} 条消息已保存到本地(联系人:${chatId})`);
renderFloatingUI(store);
if (typeof currentSelectedChatId !== 'undefined' && currentSelectedChatId === chatId) {
console.log("[content.js] �� 强制刷新悬浮窗消息界面");
window.renderFloatingUI(store);
}
});
});
}
window.addEventListener("force-scan-line", (e) => {
const currentIdRaw = getCurrentChatId();
const targetIdRaw = e.detail?.targetChatId;
const currentId = String(currentIdRaw || '').trim();
const targetId = String(targetIdRaw || '').trim();
console.log(`�� 当前页面ID=[${currentId}] (类型:${typeof currentId})`);
console.log(`�� 目标悬浮窗ID=[${targetId}] (类型:${typeof targetId})`);
if (currentId !== targetId) {
alert('⚠️ 请先进入对应联系人聊天页面后再点击获取历史记录');
console.log(`❌ ID不一致,当前页面ID=[${currentId}],目标ID=[${targetId}]。拒绝采集。`);
return;
}
console.log(`✅ ID一致,执行历史消息采集...`);
collectHistoryMessagesInChat();
});
setTimeout(() => {
scanUnreadList();
observeEachContact();
observeFriendListContainer();
}, 1500);
console.log("✅ 全功能 content.js 已启动(实时监听 + 历史采集 + 刷新同步)");
console