| | |
| | |
| | |
| | |
| |
|
| | import type { Message, User } from './types'; |
| |
|
| | export interface LocalSummary { |
| | participants: string[]; |
| | topic: string; |
| | summaryPoints: string[]; |
| | } |
| |
|
| | |
| | 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(['ุฃูุง', 'ุฃูุช', 'ูู', 'ูู', 'ูุญู', 'ูู
', 'ู', 'ุฃู', 'ูู', 'ุนูู', 'ู
ู', 'ุนู', 'ู
ุง', 'ู
ุงุฐุง', 'ููู', 'ู
ุชู', 'ุฃูู', 'ูู', 'ูุงู', 'ูููู', 'ูุงู', 'ูุงูุช', 'ูุฐุง', 'ูุฐู', 'ุฐูู', 'ุชูู']); |
| |
|
| | |
| | const KEYWORD_SCORES = { |
| | |
| | '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, |
| | |
| | 'ู
ูุงููุฉ': 5, 'ูุฑุงุฑ': 5, 'ุฎุทุฉ': 5, 'ู
ูู
': 5, 'ูุฃู': 4, 'ุณุคุงู': 4, |
| | 'ุงูุฎุทูุฉ ุงูุชุงููุฉ': 5, 'ุฃุฎูุฑุง': 4, 'ุฎูุงุตุฉ': 5, 'ุณุฃููู
': 3, 'ูุฌุจ ุนูููุง': 3, 'ุงุชูููุง': 5, |
| | }; |
| |
|
| | interface ScoredMessage { |
| | message: Message; |
| | score: number; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | function getMessageScore(message: Message, previousMessage?: Message): number { |
| | if (!message.text || message.isSystemMessage) return 0; |
| |
|
| | let score = 0; |
| | const text = message.text.toLowerCase(); |
| |
|
| | |
| | for (const keyword in KEYWORD_SCORES) { |
| | if (text.includes(keyword)) { |
| | score += KEYWORD_SCORES[keyword as keyof typeof KEYWORD_SCORES]; |
| | } |
| | } |
| |
|
| | |
| | if (text.includes('?')) { |
| | score += 5; |
| | } |
| |
|
| | |
| | if (previousMessage && previousMessage.text?.includes('?')) { |
| | score += 6; |
| | } |
| |
|
| | |
| | if (text.length > 100) { |
| | score += 3; |
| | } else if (text.length > 50) { |
| | score += 2; |
| | } |
| |
|
| | return score; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | export function generateLocalSummary(messages: Message[], currentUser: User): LocalSummary { |
| | if (messages.length === 0) { |
| | return { |
| | participants: [], |
| | topic: 'No conversation yet', |
| | summaryPoints: ['The chat is empty.'], |
| | }; |
| | } |
| |
|
| | |
| | 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); |
| |
|
| | |
| | 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), |
| | }); |
| | } |
| |
|
| | |
| | const sortedScoredMessages = scoredMessages.sort((a, b) => b.score - a.score); |
| |
|
| | |
| | const wordCounts = new Map<string, number>(); |
| | const stopWords = new Set([...STOP_WORDS_EN, ...STOP_WORDS_AR]); |
| | |
| | |
| | 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'; |
| |
|
| | |
| | const summaryPoints: string[] = []; |
| | const usedMessageIds = new Set<string>(); |
| |
|
| | |
| | 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); |
| | } |
| | } |
| |
|
| | |
| | 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, |
| | }; |
| | } |
| |
|