File size: 6,136 Bytes
cc276cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
 * @fileoverview A more intelligent, local, rule-based algorithm for summarizing chats.
 * This acts as a "primitive AI" for summarization, running entirely in the browser.
 */

import type { Message, User } from './types';

export interface LocalSummary {
    participants: string[];
    topic: string;
    summaryPoints: string[];
}

// Simple list of common "stop words" to ignore when finding topics.
const STOP_WORDS_EN = new Set(['a', 'an', 'the', 'is', 'are', 'was', 'were', 'in', 'on', 'at', 'i', 'you', 'he', 'she', 'it', 'we', 'they', 'and', 'but', 'or', 'so', 'to', 'of', 'for', 'with', 'about', 'as', 'that', 'this', 'what', 'when', 'where', 'why', 'how', 'do', 'does', 'did']);
const STOP_WORDS_AR = new Set(['ุฃู†ุง', 'ุฃู†ุช', 'ู‡ูˆ', 'ู‡ูŠ', 'ู†ุญู†', 'ู‡ู…', 'ูˆ', 'ุฃูˆ', 'ููŠ', 'ุนู„ู‰', 'ู…ู†', 'ุนู†', 'ู…ุง', 'ู…ุงุฐุง', 'ูƒูŠู', 'ู…ุชู‰', 'ุฃูŠู†', 'ู‡ู„', 'ูƒุงู†', 'ูŠูƒูˆู†', 'ู‚ุงู„', 'ู‚ุงู„ุช', 'ู‡ุฐุง', 'ู‡ุฐู‡', 'ุฐู„ูƒ', 'ุชู„ูƒ']);

// Keywords that indicate an important message
const KEYWORD_SCORES = {
    // English
    'agree': 5, 'decision': 5, 'plan': 5, 'important': 5, 'because': 4, 'question': 4,
    'next step': 5, 'finally': 4, 'conclusion': 5, 'i will': 3, 'we should': 3,
    // Arabic
    'ู…ูˆุงูู‚ุฉ': 5, 'ู‚ุฑุงุฑ': 5, 'ุฎุทุฉ': 5, 'ู…ู‡ู…': 5, 'ู„ุฃู†': 4, 'ุณุคุงู„': 4,
    'ุงู„ุฎุทูˆุฉ ุงู„ุชุงู„ูŠุฉ': 5, 'ุฃุฎูŠุฑุง': 4, 'ุฎู„ุงุตุฉ': 5, 'ุณุฃู‚ูˆู…': 3, 'ูŠุฌุจ ุนู„ูŠู†ุง': 3, 'ุงุชูู‚ู†ุง': 5,
};

interface ScoredMessage {
    message: Message;
    score: number;
}

/**
 * Scores a message based on its content to determine its importance.
 * @param message The message to score.
 * @param previousMessage The message that came before it.
 * @returns A numerical score.
 */
function getMessageScore(message: Message, previousMessage?: Message): number {
    if (!message.text || message.isSystemMessage) return 0;

    let score = 0;
    const text = message.text.toLowerCase();

    // 1. Keyword scoring
    for (const keyword in KEYWORD_SCORES) {
        if (text.includes(keyword)) {
            score += KEYWORD_SCORES[keyword as keyof typeof KEYWORD_SCORES];
        }
    }

    // 2. Question scoring
    if (text.includes('?')) {
        score += 5; // Questions are very important
    }

    // 3. Answer scoring (if it follows a question)
    if (previousMessage && previousMessage.text?.includes('?')) {
        score += 6; // Answers are even more important
    }

    // 4. Length scoring (longer messages are likely more detailed)
    if (text.length > 100) {
        score += 3;
    } else if (text.length > 50) {
        score += 2;
    }

    return score;
}

/**
 * Generates a more intelligent summary of a chat history locally.
 * @param messages The array of messages in the chat.
 * @param currentUser The currently logged-in user.
 * @returns A LocalSummary object.
 */
export function generateLocalSummary(messages: Message[], currentUser: User): LocalSummary {
    if (messages.length === 0) {
        return {
            participants: [],
            topic: 'No conversation yet',
            summaryPoints: ['The chat is empty.'],
        };
    }

    // 1. Identify Participants
    const participantSet = new Set<string>();
    messages.forEach(msg => {
        if (msg.sender === currentUser.uid) {
            participantSet.add(currentUser.displayName);
        } else {
            participantSet.add(msg.senderDisplayName);
        }
    });
    const participants = Array.from(participantSet);

    // 2. Score all messages for importance
    const scoredMessages: ScoredMessage[] = [];
    for (let i = 0; i < messages.length; i++) {
        const message = messages[i];
        const previousMessage = i > 0 ? messages[i - 1] : undefined;
        scoredMessages.push({
            message,
            score: getMessageScore(message, previousMessage),
        });
    }

    // Sort messages by score to find the most important ones
    const sortedScoredMessages = scoredMessages.sort((a, b) => b.score - a.score);

    // 3. Determine Topic from the most important messages
    const wordCounts = new Map<string, number>();
    const stopWords = new Set([...STOP_WORDS_EN, ...STOP_WORDS_AR]);
    
    // Analyze top 5 most important messages for topic words
    sortedScoredMessages.slice(0, 5).forEach(({ message }) => {
        if (!message.text) return;
        const words = message.text.toLowerCase().split(/\s+/);
        words.forEach(word => {
            const cleanWord = word.replace(/[.,!?"'()ุŸ]/g, '');
            if (cleanWord.length > 3 && !stopWords.has(cleanWord)) {
                wordCounts.set(cleanWord, (wordCounts.get(cleanWord) || 0) + 1);
            }
        });
    });

    const sortedWords = Array.from(wordCounts.entries()).sort((a, b) => b[1] - a[1]);
    const topic = sortedWords.length > 0 ? sortedWords.slice(0, 2).map(entry => entry[0]).join(' & ') : 'General discussion';

    // 4. Generate Summary Points from the top scored messages
    const summaryPoints: string[] = [];
    const usedMessageIds = new Set<string>();

    // Take top 3-4 most important messages to form the summary
    for (const scoredMsg of sortedScoredMessages) {
        if (summaryPoints.length >= 4) break;
        if (scoredMsg.score > 0 && scoredMsg.message.id && !usedMessageIds.has(scoredMsg.message.id)) {
            const point = `${scoredMsg.message.senderDisplayName} discussed: "${scoredMsg.message.text!.substring(0, 50)}...".`;
            summaryPoints.push(point);
            usedMessageIds.add(scoredMsg.message.id);
        }
    }

    // Fallback if no "important" messages were found
    if (summaryPoints.length === 0 && messages.length > 0) {
        const firstMessage = messages[0];
        const lastMessage = messages[messages.length - 1];
        summaryPoints.push(`Conversation started by ${firstMessage.senderDisplayName}.`);
        if (lastMessage.id !== firstMessage.id) {
            summaryPoints.push(`The last message was from ${lastMessage.senderDisplayName}.`);
        }
    }

    return {
        participants,
        topic,
        summaryPoints,
    };
}