mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-04-08 07:25:51 +00:00
Merge pull request #2 from Jasonzhu1207/v0/jasonzhu081207-4751-3942175b
Add AI insights service and settings tab
This commit is contained in:
@@ -17,7 +17,7 @@ import https from 'https'
|
|||||||
import http from 'http'
|
import http from 'http'
|
||||||
import { URL } from 'url'
|
import { URL } from 'url'
|
||||||
import { ConfigService } from './config'
|
import { ConfigService } from './config'
|
||||||
import { chatService } from './chatService'
|
import { chatService, ChatSession, Message } from './chatService'
|
||||||
import { showNotification } from '../windows/notificationWindow'
|
import { showNotification } from '../windows/notificationWindow'
|
||||||
|
|
||||||
// ─── 常量 ────────────────────────────────────────────────────────────────────
|
// ─── 常量 ────────────────────────────────────────────────────────────────────
|
||||||
@@ -47,12 +47,6 @@ interface TodayTriggerRecord {
|
|||||||
timestamps: number[]
|
timestamps: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InsightResult {
|
|
||||||
sessionId: string
|
|
||||||
displayName: string
|
|
||||||
insight: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── 工具函数 ─────────────────────────────────────────────────────────────────
|
// ─── 工具函数 ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,20 +106,21 @@ function callApi(
|
|||||||
stream: false
|
stream: false
|
||||||
})
|
})
|
||||||
|
|
||||||
const options: https.RequestOptions = {
|
const options = {
|
||||||
hostname: urlObj.hostname,
|
hostname: urlObj.hostname,
|
||||||
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
||||||
path: urlObj.pathname + urlObj.search,
|
path: urlObj.pathname + urlObj.search,
|
||||||
method: 'POST',
|
method: 'POST' as const,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': Buffer.byteLength(body),
|
'Content-Length': Buffer.byteLength(body).toString(),
|
||||||
Authorization: `Bearer ${apiKey}`
|
Authorization: `Bearer ${apiKey}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const transport = urlObj.protocol === 'https:' ? https : http
|
const isHttps = urlObj.protocol === 'https:'
|
||||||
const req = (transport as typeof https).request(options, (res) => {
|
const requestFn = isHttps ? https.request : http.request
|
||||||
|
const req = requestFn(options, (res) => {
|
||||||
let data = ''
|
let data = ''
|
||||||
res.on('data', (chunk) => { data += chunk })
|
res.on('data', (chunk) => { data += chunk })
|
||||||
res.on('end', () => {
|
res.on('end', () => {
|
||||||
@@ -193,9 +188,18 @@ class InsightService {
|
|||||||
|
|
||||||
stop(): void {
|
stop(): void {
|
||||||
this.started = false
|
this.started = false
|
||||||
if (this.dbDebounceTimer) { clearTimeout(this.dbDebounceTimer); this.dbDebounceTimer = null }
|
if (this.dbDebounceTimer !== null) {
|
||||||
if (this.silenceScanTimer) { clearInterval(this.silenceScanTimer); this.silenceScanTimer = null }
|
clearTimeout(this.dbDebounceTimer)
|
||||||
if (this.silenceInitialDelayTimer) { clearTimeout(this.silenceInitialDelayTimer); this.silenceInitialDelayTimer = null }
|
this.dbDebounceTimer = null
|
||||||
|
}
|
||||||
|
if (this.silenceScanTimer !== null) {
|
||||||
|
clearInterval(this.silenceScanTimer)
|
||||||
|
this.silenceScanTimer = null
|
||||||
|
}
|
||||||
|
if (this.silenceInitialDelayTimer !== null) {
|
||||||
|
clearTimeout(this.silenceInitialDelayTimer)
|
||||||
|
this.silenceInitialDelayTimer = null
|
||||||
|
}
|
||||||
console.log('[InsightService] 已停止')
|
console.log('[InsightService] 已停止')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +211,9 @@ class InsightService {
|
|||||||
if (!this.started) return
|
if (!this.started) return
|
||||||
if (!this.isEnabled()) return
|
if (!this.isEnabled()) return
|
||||||
|
|
||||||
if (this.dbDebounceTimer) clearTimeout(this.dbDebounceTimer)
|
if (this.dbDebounceTimer !== null) {
|
||||||
|
clearTimeout(this.dbDebounceTimer)
|
||||||
|
}
|
||||||
this.dbDebounceTimer = setTimeout(() => {
|
this.dbDebounceTimer = setTimeout(() => {
|
||||||
this.dbDebounceTimer = null
|
this.dbDebounceTimer = null
|
||||||
void this.analyzeRecentActivity()
|
void this.analyzeRecentActivity()
|
||||||
@@ -320,14 +326,15 @@ class InsightService {
|
|||||||
const sessionsResult = await chatService.getSessions()
|
const sessionsResult = await chatService.getSessions()
|
||||||
if (!sessionsResult.success || !sessionsResult.sessions) return
|
if (!sessionsResult.success || !sessionsResult.sessions) return
|
||||||
|
|
||||||
const sessions = sessionsResult.sessions as any[]
|
const sessions: ChatSession[] = sessionsResult.sessions
|
||||||
|
|
||||||
for (const session of sessions) {
|
for (const session of sessions) {
|
||||||
const sessionId = String(session.username || '').trim()
|
const sessionId = session.username?.trim() || ''
|
||||||
if (!sessionId || sessionId.endsWith('@chatroom')) continue // 跳过群聊
|
if (!sessionId || sessionId.endsWith('@chatroom')) continue // 跳过群聊
|
||||||
if (sessionId.toLowerCase().includes('placeholder')) continue
|
if (sessionId.toLowerCase().includes('placeholder')) continue
|
||||||
|
|
||||||
const lastTimestamp = Number(session.lastTimestamp || 0) * (String(session.lastTimestamp).length <= 10 ? 1000 : 1)
|
// lastTimestamp 单位是秒,需要转换为毫秒
|
||||||
|
const lastTimestamp = (session.lastTimestamp || 0) * 1000
|
||||||
if (!lastTimestamp || lastTimestamp <= 0) continue
|
if (!lastTimestamp || lastTimestamp <= 0) continue
|
||||||
|
|
||||||
const silentMs = now - lastTimestamp
|
const silentMs = now - lastTimestamp
|
||||||
@@ -366,18 +373,18 @@ class InsightService {
|
|||||||
const sessionsResult = await chatService.getSessions()
|
const sessionsResult = await chatService.getSessions()
|
||||||
if (!sessionsResult.success || !sessionsResult.sessions) return
|
if (!sessionsResult.success || !sessionsResult.sessions) return
|
||||||
|
|
||||||
const sessions = sessionsResult.sessions as any[]
|
const sessions: ChatSession[] = sessionsResult.sessions
|
||||||
// 只取最近有活动的前 5 个会话(排除群聊以降低噪音,可按需调整)
|
// 只取最近有活动的前 5 个会话(排除群聊以降低噪音,可按需调整)
|
||||||
const candidates = sessions
|
const candidates = sessions
|
||||||
.filter((s) => {
|
.filter((s) => {
|
||||||
const id = String(s.username || '').trim()
|
const id = s.username?.trim() || ''
|
||||||
return id && !id.endsWith('@chatroom') && !id.toLowerCase().includes('placeholder')
|
return id && !id.endsWith('@chatroom') && !id.toLowerCase().includes('placeholder')
|
||||||
})
|
})
|
||||||
.slice(0, 5)
|
.slice(0, 5)
|
||||||
|
|
||||||
for (const session of candidates) {
|
for (const session of candidates) {
|
||||||
await this.generateInsightForSession({
|
await this.generateInsightForSession({
|
||||||
sessionId: String(session.username || '').trim(),
|
sessionId: session.username?.trim() || '',
|
||||||
displayName: session.displayName || session.username,
|
displayName: session.displayName || session.username,
|
||||||
triggerReason: 'activity'
|
triggerReason: 'activity'
|
||||||
})
|
})
|
||||||
@@ -418,13 +425,14 @@ class InsightService {
|
|||||||
try {
|
try {
|
||||||
const msgsResult = await chatService.getLatestMessages(sessionId, MAX_CONTEXT_MESSAGES)
|
const msgsResult = await chatService.getLatestMessages(sessionId, MAX_CONTEXT_MESSAGES)
|
||||||
if (msgsResult.success && msgsResult.messages && msgsResult.messages.length > 0) {
|
if (msgsResult.success && msgsResult.messages && msgsResult.messages.length > 0) {
|
||||||
const msgLines = (msgsResult.messages as any[]).map((m) => {
|
const messages: Message[] = msgsResult.messages
|
||||||
|
const msgLines = messages.map((m) => {
|
||||||
const sender = m.isSend === 1 ? '我' : (displayName || sessionId)
|
const sender = m.isSend === 1 ? '我' : (displayName || sessionId)
|
||||||
const content = m.rawContent || m.parsedContent || '[非文字消息]'
|
const content = m.rawContent || m.parsedContent || '[非文字消息]'
|
||||||
const time = new Date(Number(m.createTime) * 1000).toLocaleString('zh-CN')
|
const time = new Date(Number(m.createTime) * 1000).toLocaleString('zh-CN')
|
||||||
return `[${time}] ${sender}:${content}`
|
return `[${time}] ${sender}:${content}`
|
||||||
})
|
})
|
||||||
contextSection = `\n\n近期对话记录(最近 ${msgLines.length} 条):\n${msgLines.join('\n')}`
|
contextSection = `\n\n<EFBFBD><EFBFBD>期对话记录(最近 ${msgLines.length} 条):\n${msgLines.join('\n')}`
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[InsightService] 拉取上下文失败:', e)
|
console.warn('[InsightService] 拉取上下文失败:', e)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
"appId": "com.WeFlow.app",
|
"appId": "com.WeFlow.app",
|
||||||
"publish": {
|
"publish": {
|
||||||
"provider": "github",
|
"provider": "github",
|
||||||
"owner": "hicccc77",
|
"owner": "Jasonzhu1207",
|
||||||
"repo": "WeFlow",
|
"repo": "WeFlow",
|
||||||
"releaseType": "release"
|
"releaseType": "release"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user