SOURCE

let tempMessageCounter = 1;
const throttledScan = throttle(() => {
   console.log("⚡ 节流触发:刷新好友列表扫描");
   scanUnreadList();
}, 2000);  // 每2秒最多刷新1次

// ✅ 节流函数(保留)
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
  };
}

// ✅ 完整:processSingleContact - 去重 + 已读过滤 + 直接写入历史
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 命令行工具 X clear

                    
>
console