Telegram AI 聊天摘要笔记助手 (v2.2.0-fix16b - 高 Tokens 完整)

AI结合现有笔记和新消息,仅提取指定类别核心信息,使用消息精确时间戳,替换旧摘要块。设置高 max_tokens。

目前为 2025-04-25 提交的版本,查看 最新版本

// ==UserScript==
// @name         Telegram AI 聊天摘要笔记助手 (v2.2.0-fix16b - 高 Tokens 完整)
// @namespace    http://tampermonkey.net/
// @version      2.2.0-aisync-fix16b-full // 设置 max_tokens=30000 (完整代码)
// @description  AI结合现有笔记和新消息,仅提取指定类别核心信息,使用消息精确时间戳,替换旧摘要块。设置高 max_tokens。
// @author       萧遥
// @match        https://web.telegram.org/k/*
// @match        https://web.telegram.org/a/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_info
// @grant        GM_xmlhttpRequest
// @connect      api.ohmygpt.com
// ==/UserScript==

(function() {
    'use strict';

    // --- 配置 ---
    const NOTES_AREA_ID = 'userscript-tg-chat-notes-ai-summary';
    const STORAGE_KEY_PREFIX = 'tg_notes_';
    const DEBOUNCE_DELAY = 750;
    const OHMYGPT_API_KEY = "sk-RK1MU6Cg6a48fBecBBADT3BlbKFJ4C209a954d3b4428b54b"; // 使用你原来的 Key
    const OHMYGPT_API_ENDPOINT = "https://api.ohmygpt.com/v1/chat/completions";
    // ★★★ 仍然使用你指定的模型,如果 API 报错请更换 ★★★
    const OHMYGPT_MODEL = "gemini-2.5-flash-preview-04-17-thinking-disabled";
    const SUMMARY_HEADER_MARKER = "--- AI 摘要关键点"; // 用于查找和替换旧摘要的标记

    // --- 图标 ---
    const NOTEBOOK_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-journal-text" viewBox="0 0 16 16" style="vertical-align: -1px; margin-right: 4px;"><path d="M5 10.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1-.5-.5zm0-2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm0-2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm0-2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z"/><path d="M3 0h10a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-1h1v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v1H1V2a2 2 0 0 1 2-2z"/><path d="M1 5v-.5a.5.5 0 0 1 1 0V5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1H1zm0 3v-.5a.5.5 0 0 1 1 0V8h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1H1zm0 3v-.5a.5.5 0 0 1 1 0v.5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1H1z"/></svg>`;
    const SUMMARY_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-body-text" viewBox="0 0 16 16" style="vertical-align: -2px; margin-right: 3px;"><path fill-rule="evenodd" d="M0 .5A.5.5 0 0 1 .5 0h4a.5.5 0 0 1 0 1h-4A.5.5 0 0 1 0 .5m0 2A.5.5 0 0 1 .5 2h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 0 2.5m0 2A.5.5 0 0 1 .5 4h11a.5.5 0 0 1 0 1h-11A.5.5 0 0 1 0 4.5m0 2A.5.5 0 0 1 .5 6h11a.5.5 0 0 1 0 1h-11A.5.5 0 0 1 0 6.5m0 2A.5.5 0 0 1 .5 8h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 0 8.5m0 2A.5.5 0 0 1 .5 10h4a.5.5 0 0 1 0 1h-4A.5.5 0 0 1 0 10.5m0 2A.5.5 0 0 1 .5 12h11a.5.5 0 0 1 0 1h-11A.5.5 0 0 1 0 12.5m0 2A.5.5 0 0 1 .5 14h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 0 14.5"/></svg>`;

    // --- 样式 ---
    GM_addStyle(`
        .chat-view, #column-center, .MiddleColumn { position: relative !important; }
        #${NOTES_AREA_ID} {
            position: absolute; top: 50px; right: 20px;
            width: 250px; max-width: 40%;
            z-index: 105; display: none; flex-direction: column;
            padding: 8px 10px; border: 1px solid var(--border-color, #ccc);
            border-radius: 8px; background-color: var(--background-color-secondary, rgba(248, 248, 248, 0.94));
            box-shadow: 0 1px 4px rgba(0,0,0,0.18); opacity: 0.85;
            transition: opacity 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
            overflow: hidden; max-height: calc(100vh - 100px);
        }
        #${NOTES_AREA_ID}.visible { display: flex; }
        #${NOTES_AREA_ID}:hover, #${NOTES_AREA_ID}:focus-within { opacity: 1; box-shadow: 0 2px 6px rgba(0,0,0,0.25); }
        #${NOTES_AREA_ID} .notes-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; padding-bottom: 3px; border-bottom: 1px dashed var(--border-color, #ccc); flex-shrink: 0; }
        #${NOTES_AREA_ID} .notes-label { font-size: 0.75em; font-weight: bold; color: var(--secondary-text-color, #444); display: flex; align-items: center; user-select: none; cursor: default; }
        #${NOTES_AREA_ID} .ai-summary-button { font-size: 0.75em; padding: 2px 5px; cursor: pointer; border: 1px solid var(--accent-color-secondary, #aaa); border-radius: 5px; background-color: var(--button-secondary-background, #eee); color: var(--secondary-text-color, #333); transition: background-color 0.2s; display: inline-flex; align-items: center; line-height: 1.1; }
        #${NOTES_AREA_ID} .ai-summary-button:hover { background-color: var(--button-secondary-background-hover, #ddd); }
        #${NOTES_AREA_ID} .ai-summary-button:disabled { opacity: 0.6; cursor: not-allowed; }
        #${NOTES_AREA_ID} .ai-summary-button.loading { cursor: wait; background-color: #f0ad4e; color: white; border-color: #eea236; animation: pulse 1.5s infinite ease-in-out; }
        #${NOTES_AREA_ID} .ai-summary-button.error { background-color: #d9534f; color: white; border-color: #d43f3a; }
        @keyframes pulse { 0% { opacity: 0.7; } 50% { opacity: 1; } 100% { opacity: 0.7; } }
        #${NOTES_AREA_ID} textarea { width: 100%; min-height: 60px; height: auto; resize: vertical; border: none; outline: none; background-color: transparent; color: var(--text-color, #000); font-size: 0.9em; line-height: 1.3; font-family: inherit; flex-grow: 1; margin-top: 3px; scrollbar-width: thin; scrollbar-color: var(--accent-color, #888) transparent; }
        #${NOTES_AREA_ID} textarea::-webkit-scrollbar { width: 6px; }
        #${NOTES_AREA_ID} textarea::-webkit-scrollbar-track { background: transparent; }
        #${NOTES_AREA_ID} textarea::-webkit-scrollbar-thumb { background-color: var(--accent-color, #888); border-radius: 3px; }
    `);

    let saveTimeout = null;
    let currentChatId = null;
    let isAISummarizing = false;

    // --- 辅助函数 ---
    function getChatIdFromHash() { const hash = window.location.hash; if (hash && hash.length > 1) { const match = hash.match(/^#\/(?:k|a)\/([\w-]+)/); if (match) return match[1]; const mainId = hash.substring(1).split('?')[0]; return mainId; } return null; }
    function getStorageKey(chatId) { if (!chatId) return null; return `${STORAGE_KEY_PREFIX}${chatId}`; }
    function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => { func.apply(this, args); }, delay); }; }
    function formatUnixTimestamp(unixTimestamp) { if (!unixTimestamp) return null; try { const date = new Date(parseInt(unixTimestamp, 10) * 1000); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; } catch (e) { console.error("Error formatting timestamp:", e); return null; } }

    // --- 核心逻辑 ---
    function saveNotes(notesValue) { if (currentChatId) { const storageKey = getStorageKey(currentChatId); if (storageKey) { GM_setValue(storageKey, notesValue); } } }
    const debouncedSaveNotes = debounce(saveNotes, DEBOUNCE_DELAY);
    function findChatViewContainer() { let container = document.querySelector('.chat-view'); if (container) return container; container = document.querySelector('#column-center'); if (container) return container; container = document.querySelector('.MessageList'); if (container && container.parentElement) { return container.parentElement; } container = document.querySelector('.MiddleColumn'); if (container) { console.log("Using .MiddleColumn as container fallback"); return container; } console.warn("[TG Notes Sum v2.2.0-aisync-fix16b-full] Could not find suitable chat view container element using v2.2.0 logic + fallback."); return null; }

    // --- 文本提取逻辑 ---
    function extractTextFromBubble(elementToExtractFrom) {
        // console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full Extract Debug] Trying to extract text from element:", elementToExtractFrom);
        if (!elementToExtractFrom) return '';
        let text = '';
        const translationWrapper = elementToExtractFrom.querySelector('font.immersive-translate-target-translation-block-wrapper');
        if (translationWrapper) {
            const clone = translationWrapper.cloneNode(true);
            clone.querySelectorAll('.gpt-controls-container, .gpt-controls-container-wa, .persona-selector, .gpt-api-button, .userscript-tg-chat-notes-ai, .ai-summary-button, button, svg, img, span.time, .message-sign').forEach(el => el.remove());
            text = clone.textContent?.trim() || '';
            if (text) { return text; }
        }
        const originalTextSpan = elementToExtractFrom.querySelector('span.translatable-message');
        if (originalTextSpan) {
            const clone = originalTextSpan.cloneNode(true);
            clone.querySelectorAll('font.notranslate, span.time, span.message-reactions, div.message-reactions, span.hidden-copy-text, button, svg, img, .message-sign').forEach(el => el.remove());
            text = clone.textContent?.trim() || '';
            if (text) { return text; }
        }
        const clone = elementToExtractFrom.cloneNode(true);
        clone.querySelectorAll('span.time, span.message-reactions, div.message-reactions, button, svg, img, .bubble-tail, .bubble-actions, .message-sign, .message-views, .message-forwarded, .reply-markup, .reactions-container, font, .hidden-copy-text').forEach(el => el.remove());
        text = clone.textContent?.trim().replace(/\s+/g, ' ') || '';
        if (text) { return text; }
        else { console.warn("[TG Notes Sum v2.2.0-aisync-fix16b-full Extract Debug] Failed: All extraction methods failed for element:", elementToExtractFrom.innerHTML.substring(0, 200) + "..."); }
        return '';
     }

    // --- 提取时间戳逻辑 ---
    function extractTimestampFromMessageItem(item) {
        if (!item) return null;
        const dataTimestamp = item.dataset.timestamp;
        if (dataTimestamp) {
            const formattedTime = formatUnixTimestamp(dataTimestamp);
            if(formattedTime) return formattedTime;
        }
        const timeElement = item.querySelector('.bubble-content .message-sign .time, .bubble-content > .time');
        if (timeElement) { return timeElement.textContent?.trim() || null; }
        const timeElementFallback = item.querySelector('.time');
         if (timeElementFallback) { return timeElementFallback.textContent?.trim() || null; }
        return null;
    }

    // --- 获取最新传入消息文本 ---
    function getLastIncomingMessagesText() {
        console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full Debug] 开始执行 getLastIncomingMessagesText...");
        const messageItemSelector = 'div[data-mid]'; const incomingClass = 'is-in'; const contentSelector = '.bubble-content';
        const chatAreaSelector = '#column-center, .MiddleColumn'; let searchContext = document; const chatArea = document.querySelector(chatAreaSelector);
        if (chatArea) { searchContext = chatArea; } else { console.warn(`[TG Notes Sum v2.2.0-aisync-fix16b-full Debug] 未找到聊天区域,在整个文档查找 "${messageItemSelector}"。`); }
        const allMessageItems = searchContext.querySelectorAll(messageItemSelector); console.log(`[TG Notes Sum v2.2.0-aisync-fix16b-full Debug] 找到 ${allMessageItems.length} 个消息项。`);
        if (allMessageItems.length === 0) { console.error(`[TG Notes Sum v2.2.0-aisync-fix16b-full Debug] 未找到任何消息项 ("${messageItemSelector}")。`); return null; }
        const messages = []; let lastMessageTimestamp = null; let incomingCount = 0; let checkedCount = 0; console.log(`[TG Notes Sum v2.2.0-aisync-fix16b-full Debug] 从 ${allMessageItems.length} 项中倒序查找所有对方消息...`);
        for (let i = allMessageItems.length - 1; i >= 0; i--) {
            checkedCount++; const item = allMessageItems[i]; const isIncoming = item.classList.contains(incomingClass);
            if (isIncoming) {
                const contentElement = item.querySelector(contentSelector);
                if (contentElement) {
                     const text = extractTextFromBubble(contentElement);
                     if (text) {
                         messages.unshift(text);
                         if (incomingCount === 0) { lastMessageTimestamp = extractTimestampFromMessageItem(item); }
                         incomingCount++;
                     } else { console.warn(`[TG Notes Sum v2.2.0-aisync-fix16b-full Debug]   警告: Item ${i} 被识别为接收,找到了 Content (${contentSelector}),但未能提取文本。`); }
                } else { console.warn(`[TG Notes Sum v2.2.0-aisync-fix16b-full Debug]   警告: Item ${i} 被识别为接收消息,但未找到其内部的 "${contentSelector}" 元素。`); }
            }
        }
        console.log(`[TG Notes Sum v2.2.0-aisync-fix16b-full Debug] 循环结束。总共检查了 ${checkedCount} 条消息项。`);
        if (messages.length === 0) { console.error(`[TG Notes Sum v2.2.0-aisync-fix16b-full Debug] 错误: 遍历了 ${checkedCount} 条消息,但未能提取到文本。`); return null; }
        console.log(`[TG Notes Sum v2.2.0-aisync-fix16b-full Debug] 成功提取 ${messages.length} 条接收消息。最后一条消息时间戳: ${lastMessageTimestamp}`);
        return { text: messages.join('\n\n'), lastTimestamp: lastMessageTimestamp };
    }

    // --- 构造 AI Prompt ---
    function createSummaryPrompt(existingNotes, newMessagesText) {
        let previousSummaryPoints = "";
        const lastSummaryStartIndex = existingNotes.lastIndexOf(SUMMARY_HEADER_MARKER);
        if (lastSummaryStartIndex !== -1) {
            const previousBlock = existingNotes.substring(lastSummaryStartIndex);
            const lines = previousBlock.split('\n');
            const points = lines.filter(line => line.trim().startsWith('*') || line.trim().startsWith('-')).map(line => line.trim()).join('\n');
            if (points) {
                previousSummaryPoints = points;
                console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full Prompt Debug] 找到了之前的摘要要点用于上下文:\n", previousSummaryPoints);
            } else {
                 console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full Prompt Debug] 找到旧摘要标记,但未能提取有效要点。");
            }
        } else {
             console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full Prompt Debug] 未找到之前的摘要标记,将首次生成摘要。");
        }

        let prompt = `你是一个极其精准和简洁的信息提取与整合助手。请回顾【历史关键信息点】(如果为空则忽略),并分析【最新客户消息】。

你的任务是生成一份【更新后的完整关键信息列表】,**仅包含且必须包含**以下类别的信息点,如果信息存在的话:
*   **个人信息**: **核心特征**,如客户年龄(若提及)、主要职业身份、常驻城市/国家。**忽略**临时状态如“刚到家/机场/正在吃饭”。
*   **家人信息**: 提及的配偶、子女、父母、兄弟姐妹等及其**关键情况**(例如:儿子在XX大学读书,女儿计划一起晚餐)。仅记录关系和核心信息。
*   **财务信息**: 提及的**具体**投资领域(如:股票、贵金属)、未投资领域(如:比特币)、市场看法、收入来源、重要财务决策或问题。
*   **个人爱好**: 明确表示的**长期兴趣或习惯**(如:喜欢高尔夫、阅读、烹饪特定菜肴、有晨间冥想习惯)。**忽略**一次性的活动或普通的饮食(如“吃了三明治”)。
*   **宗教信仰**: 明确提及的宗教归属、实践或相关价值观。
*   **痛点**: 明确表达的**持续性或重要的**不满、担忧、困难或挑战(例如:工作繁忙影响规划、朋友重病需照顾)。**忽略**短暂的不便。
*   **刚需点**: 明确提出的**强烈愿望、长期目标、梦想或必须解决的问题**(例如:退休后的生活目标、重要的业务需求)。**忽略**日常计划或普通意愿(如“需要休息”、“要开会”)。
*   **感情史**: 提及的恋爱、婚姻状况。
*   **历史/背景**: 提及的**带有时间标记(如年份)的重大事件、重要的职业履历或人生转折点**。**忽略**近期的、不具里程碑意义的行程或活动(如“最近在旅行”)。

**请严格遵守以下规则**:
1.  **保留历史**: **必须完整保留**【历史关键信息点】中的所有信息。**禁止删除**任何旧的信息点,除非新消息明确更新或否定了它。
2.  **精确提取新增**: 从【最新客户消息】中**只提取**严格符合上述**类别定义和筛选标准**的、并且在【历史关键信息点】中**没有记录过的新的核心事实**,或者对旧信息的**明确更新**。
3.  **合并输出**: 生成一份**包含所有历史信息点和所有新增/更新信息点**的【更新后的完整关键信息列表】。
4.  **极度简洁与合并**: 每个信息点必须是**核心事实短语**。**同一类别**的信息点如果内容相关,应**尽可能合并到一行内**,用逗号或分号分隔,**避免每个点单独占一行**。
5.  **格式**: 使用清晰的要点列表('* '),按类别标题组织(例如 "**个人爱好**:")。类别标题使用 **粗体**。
6.  **无新信息处理**: 如果【最新客户消息】中**没有**任何符合类别定义的**新信息或更新**,请在【更新后的完整关键信息列表】的末尾明确添加一行:“* (近期无指定类别新信息)*”。但仍然需要输出包含所有历史信息点的列表。

---
【历史关键信息点】:
${previousSummaryPoints || "(无)"}
---
【最新客户消息】:
${newMessagesText}
---

【更新后的完整关键信息列表】 (请严格遵守规则,保留所有历史信息,仅增加新的核心事实短语,合并同一类别信息点):`;

        return prompt;
    }

    // --- 调用 OhMyGPT API ---
    function callOhMyGptForSummary(prompt, callback) {
        if (!OHMYGPT_API_KEY || !OHMYGPT_API_KEY.startsWith("sk-")) { console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] OhMyGPT API 密钥丢失或无效。"); callback(null, "API 密钥丢失或无效"); return; }
        if (!prompt) { console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] API 调用收到空 Prompt。"); callback(null, "没有可摘要的消息"); return; }
        console.log(`[TG Notes Sum v2.2.0-aisync-fix16b-full] 正在调用 OhMyGPT API (模型: ${OHMYGPT_MODEL}, max_tokens: 30000)...`);
        GM_xmlhttpRequest({
            method: "POST", url: OHMYGPT_API_ENDPOINT, headers: { "Content-Type": "application/json", "Authorization": `Bearer ${OHMYGPT_API_KEY}` },
            data: JSON.stringify({
                 model: OHMYGPT_MODEL,
                 messages: [{"role": "user", "content": prompt }],
                 temperature: 0.1,
                 max_tokens: 30000 // 设置为 30000
            }),
            timeout: 120000, // 增加超时时间到 120 秒
            onload: function(response) {
                 try {
                    if (response.status >= 200 && response.status < 300) { const data = JSON.parse(response.responseText); const reply = data.choices?.[0]?.message?.content?.trim(); if (reply) callback(reply); else { console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] API 响应错误:", data); callback(null, data.error?.message || "无效响应结构"); } }
                    else { console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] API 请求失败,状态码:", response.status, response.responseText); let errorMsg = `HTTP 错误 ${response.status}`; try { const errorData = JSON.parse(response.responseText); errorMsg = errorData.error?.message || errorMsg; } catch (e) {} callback(null, errorMsg); }
                } catch (e) { console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] 解析 API 响应出错:", e); callback(null, "解析 API 响应失败"); }
             },
            onerror: function(response) { console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] API 请求 onerror:", response); callback(null, `网络错误: ${response.statusText || '未知'}`); },
            ontimeout: function() { console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] API 请求超时。"); callback(null, "API 请求超时 (120s)"); }
        });
     }

    // --- AI 摘要按钮点击处理 ---
    function handleAiSummaryClick(event) {
        const button = event.currentTarget; const notesContainer = document.getElementById(NOTES_AREA_ID); const notesTextarea = notesContainer?.querySelector('textarea');
        if (isAISummarizing || !notesTextarea || button.disabled) return;
        console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full] AI 摘要按钮点击。"); const messageData = getLastIncomingMessagesText();
        if (!messageData || !messageData.text) { alert("未能找到足够的可供分析的对方消息。\n请确保聊天记录中有对方的消息 (可向上滚动),并检查控制台日志。"); console.warn("[TG Notes Sum v2.2.0-aisync-fix16b-full] 未找到用于摘要的对方消息文本。"); return; }
        const existingNotes = notesTextarea.value; console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full] 获取到现有笔记内容用于上下文。");
        isAISummarizing = true; const originalButtonHTML = button.innerHTML; button.innerHTML = `${SUMMARY_ICON_SVG} 更新中...`; button.classList.add('loading'); button.classList.remove('error'); button.disabled = true; button.title = "正在结合上下文更新摘要...";
        const prompt = createSummaryPrompt(existingNotes, messageData.text); callOhMyGptForSummary(prompt, (result, error) => {
            isAISummarizing = false; const currentButton = notesContainer?.querySelector('.ai-summary-button'); if (!currentButton) return;
            currentButton.classList.remove('loading'); currentButton.disabled = false; currentButton.innerHTML = originalButtonHTML;
            if (result) {
                console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full] 收到更新后的摘要结果:", result);
                const generationTimestamp = new Date().toLocaleString('zh-CN', { hour12: false });
                const timeInfo = messageData.lastTimestamp ? `截至 ${messageData.lastTimestamp} 的消息` : '近期消息';
                const newSummaryBlock = `${SUMMARY_HEADER_MARKER} (更新于 ${generationTimestamp}, 基于${timeInfo}) ---\n${result.trim()}`;
                let newNotesContent = "";
                const lastSummaryIndex = existingNotes.lastIndexOf(SUMMARY_HEADER_MARKER);
                if (lastSummaryIndex !== -1) {
                    newNotesContent = existingNotes.substring(0, lastSummaryIndex).trimEnd();
                    console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full] 找到旧摘要标记,将替换其后的内容。");
                } else {
                    newNotesContent = existingNotes.trimEnd();
                    console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full] 未找到旧摘要标记,将在末尾追加新摘要。");
                }
                const separator = newNotesContent === '' ? '' : '\n\n';
                notesTextarea.value = newNotesContent + separator + newSummaryBlock;
                debouncedSaveNotes(notesTextarea.value);
                notesTextarea.scrollTop = notesTextarea.scrollHeight;
                currentButton.title = `摘要已更新,包含指定类别信息`;
            } else {
                console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] 摘要失败:", error);
                currentButton.classList.add('error'); currentButton.title = `AI 摘要错误: ${error || '未知错误'}. 点击重试。`;
                setTimeout(() => { currentButton.classList.remove('error'); currentButton.title = `结合现有笔记和新消息,更新摘要`; }, 5000);
            }
        });
    }

    // --- UI 注入与更新 ---
    function injectOrUpdateNotesUI() {
        const newChatId = getChatIdFromHash(); let notesContainer = document.getElementById(NOTES_AREA_ID);
        if (!newChatId) { if (notesContainer) notesContainer.classList.remove('visible'); currentChatId = null; return; }
        if (newChatId === currentChatId && notesContainer) { if (!notesContainer.classList.contains('visible')) notesContainer.classList.add('visible'); return; }
        currentChatId = newChatId; const chatViewContainer = findChatViewContainer();
        if (!chatViewContainer) { if (notesContainer) notesContainer.classList.remove('visible'); console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] Chat View Container not found by v2.2.0 logic."); return; }
        let notesTextarea; let aiSummaryButton;
        if (!notesContainer) {
            console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full] Creating notes UI elements...");
            notesContainer = document.createElement('div'); notesContainer.id = NOTES_AREA_ID;
            const headerDiv = document.createElement('div'); headerDiv.className = 'notes-header';
            const labelDiv = document.createElement('div'); labelDiv.className = 'notes-label'; labelDiv.innerHTML = `${NOTEBOOK_ICON_SVG} 聊天笔记:`; headerDiv.appendChild(labelDiv);
            aiSummaryButton = document.createElement('button'); aiSummaryButton.type = 'button'; aiSummaryButton.className = 'ai-summary-button'; aiSummaryButton.innerHTML = `${SUMMARY_ICON_SVG} 更新摘要`; aiSummaryButton.title = `结合现有笔记和新消息,更新指定类别摘要`;
            aiSummaryButton.addEventListener('click', handleAiSummaryClick); headerDiv.appendChild(aiSummaryButton);
            notesContainer.appendChild(headerDiv);
            notesTextarea = document.createElement('textarea'); notesTextarea.placeholder = '输入笔记... 点击 "更新摘要" 更新指定信息。'; notesContainer.appendChild(notesTextarea);
            notesTextarea.addEventListener('input', (event) => debouncedSaveNotes(event.target.value));
            notesTextarea.addEventListener('keydown', (event) => event.stopPropagation());
            chatViewContainer.appendChild(notesContainer); console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full] Notes UI appended.");
        } else {
            if (notesContainer.parentElement !== chatViewContainer) { console.log("[TG Notes Sum v2.2.0-aisync-fix16b-full] Moving notes UI to new container."); chatViewContainer.appendChild(notesContainer); }
            notesTextarea = notesContainer.querySelector('textarea'); aiSummaryButton = notesContainer.querySelector('.ai-summary-button');
            aiSummaryButton.title = `结合现有笔记和新消息,更新指定类别摘要`; aiSummaryButton.innerHTML = `${SUMMARY_ICON_SVG} 更新摘要`;
        }
        notesContainer.classList.add('visible');
        if (notesTextarea) {
            const storageKey = getStorageKey(currentChatId); if (storageKey) { const savedNotes = GM_getValue(storageKey, ''); if (notesTextarea.value !== savedNotes) notesTextarea.value = savedNotes; } else { notesTextarea.value = ''; }
            if(aiSummaryButton){ isAISummarizing = false; aiSummaryButton.disabled = false; aiSummaryButton.classList.remove('loading', 'error'); aiSummaryButton.innerHTML = `${SUMMARY_ICON_SVG} 更新摘要`; aiSummaryButton.title = `结合现有笔记和新消息,更新指定类别摘要`; }
        } else { console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] Textarea not found after UI update/create."); }
    }

    // --- 初始化 ---
     function initializeScript() {
         try { injectOrUpdateNotesUI(); } catch (e) { console.error("[TG Notes Sum v2.2.0-aisync-fix16b-full] Initial UI injection error:", e); }
         window.addEventListener('hashchange', injectOrUpdateNotesUI);
         const observeTargetNode = document.body;
         if (observeTargetNode) {
              const observerDebouncedCheck = debounce(injectOrUpdateNotesUI, 250);
              const observer = new MutationObserver((mutations) => { if (mutations.some(m => m.addedNodes.length > 0 || m.removedNodes.length > 0)) { observerDebouncedCheck(); } });
              observer.observe(observeTargetNode, { childList: true, subtree: true });
         }
         console.log(`[TG Notes Summarizer] Script v${GM_info.script.version} (aisync-fix16b-full) initialized using v2.2.0 UI logic.`);
     }

    // --- 启动 ---
    const initialLoadDelay = 3000;
    setTimeout(initializeScript, initialLoadDelay);

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址