mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-04-08 15:08:44 +00:00
Merge pull request #10 from Jasonzhu1207/v0/jasonzhu081207-4751-c8eef8af
Enable AI insights and Telegram push notifications
This commit is contained in:
@@ -427,17 +427,22 @@ class InsightService {
|
|||||||
// ── 沉默联系人扫描 ──────────────────────────────────────────────────────────
|
// ── 沉默联系人扫描 ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private scheduleSilenceScan(): void {
|
private scheduleSilenceScan(): void {
|
||||||
this.silenceInitialDelayTimer = setTimeout(() => {
|
// 等待扫描完成后再安排下一次,避免并发堆积
|
||||||
void this.runSilenceScan()
|
|
||||||
// 每次扫描完毕后重新读取间隔配置,允许用户动态调整不需要重启
|
|
||||||
const scheduleNext = () => {
|
const scheduleNext = () => {
|
||||||
|
if (!this.started) return
|
||||||
const intervalHours = (this.config.get('aiInsightScanIntervalHours') as number) || 4
|
const intervalHours = (this.config.get('aiInsightScanIntervalHours') as number) || 4
|
||||||
const intervalMs = Math.max(0.1, intervalHours) * 60 * 60 * 1000
|
const intervalMs = Math.max(0.1, intervalHours) * 60 * 60 * 1000
|
||||||
insightLog('INFO', `下次沉默扫描将在 ${intervalHours} 小时后执行`)
|
insightLog('INFO', `下次沉默扫描将在 ${intervalHours} 小时后执行`)
|
||||||
this.silenceScanTimer = setTimeout(() => {
|
this.silenceScanTimer = setTimeout(async () => {
|
||||||
void this.runSilenceScan().then(scheduleNext)
|
this.silenceScanTimer = null
|
||||||
|
await this.runSilenceScan()
|
||||||
|
scheduleNext()
|
||||||
}, intervalMs)
|
}, intervalMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.silenceInitialDelayTimer = setTimeout(async () => {
|
||||||
|
this.silenceInitialDelayTimer = null
|
||||||
|
await this.runSilenceScan()
|
||||||
scheduleNext()
|
scheduleNext()
|
||||||
}, SILENCE_SCAN_INITIAL_DELAY_MS)
|
}, SILENCE_SCAN_INITIAL_DELAY_MS)
|
||||||
}
|
}
|
||||||
@@ -711,8 +716,9 @@ class InsightService {
|
|||||||
const telegramChatIds = (this.config.get('aiInsightTelegramChatIds') as string) || ''
|
const telegramChatIds = (this.config.get('aiInsightTelegramChatIds') as string) || ''
|
||||||
if (telegramToken && telegramChatIds) {
|
if (telegramToken && telegramChatIds) {
|
||||||
const chatIds = telegramChatIds.split(',').map((s) => s.trim()).filter(Boolean)
|
const chatIds = telegramChatIds.split(',').map((s) => s.trim()).filter(Boolean)
|
||||||
|
const telegramText = `【WeFlow】 ${notifTitle}\n\n${insight}`
|
||||||
for (const chatId of chatIds) {
|
for (const chatId of chatIds) {
|
||||||
this.sendTelegram(telegramToken, chatId, `${notifTitle}\n\n${insight}`).catch((e) => {
|
this.sendTelegram(telegramToken, chatId, telegramText).catch((e) => {
|
||||||
insightLog('WARN', `Telegram 推送失败 (chatId=${chatId}): ${(e as Error).message}`)
|
insightLog('WARN', `Telegram 推送失败 (chatId=${chatId}): ${(e as Error).message}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1467,7 +1467,7 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group quote-layout-group">
|
<div className="form-group quote-layout-group">
|
||||||
<label><EFBFBD><EFBFBD><EFBFBD>用消息样式</label>
|
<label>引用消息样式</label>
|
||||||
<span className="form-hint">选择聊天中引用消息与正文的上下顺序,下方预览会同步展示布局差异。</span>
|
<span className="form-hint">选择聊天中引用消息与正文的上下顺序,下方预览会同步展示布局差异。</span>
|
||||||
<div className="quote-layout-picker" role="radiogroup" aria-label="引用样式选择">
|
<div className="quote-layout-picker" role="radiogroup" aria-label="引用样式选择">
|
||||||
{[
|
{[
|
||||||
@@ -2825,6 +2825,10 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
|
|||||||
2. 控制在 80 字以内,直接、具体、一针见血。不要废话。
|
2. 控制在 80 字以内,直接、具体、一针见血。不要废话。
|
||||||
3. 输出纯文本,不使用 Markdown。
|
3. 输出纯文本,不使用 Markdown。
|
||||||
4. 只有在完全没有任何可说的内容时(比如对话只有一条"嗯"),才回复"SKIP"。绝大多数情况下你应该输出见解。`
|
4. 只有在完全没有任何可说的内容时(比如对话只有一条"嗯"),才回复"SKIP"。绝大多数情况下你应该输出见解。`
|
||||||
|
|
||||||
|
// 展示值:有自定义内容时显示自定义内容,否则显示默认值(可直接编辑)
|
||||||
|
const displayValue = aiInsightSystemPrompt || DEFAULT_SYSTEM_PROMPT
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
|
||||||
@@ -2833,6 +2837,7 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
|
|||||||
className="button-secondary"
|
className="button-secondary"
|
||||||
style={{ fontSize: 12, padding: '3px 10px' }}
|
style={{ fontSize: 12, padding: '3px 10px' }}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
|
// 恢复默认:清空自定义值,UI 回到显示默认内容的状态
|
||||||
setAiInsightSystemPrompt('')
|
setAiInsightSystemPrompt('')
|
||||||
await configService.setAiInsightSystemPrompt('')
|
await configService.setAiInsightSystemPrompt('')
|
||||||
}}
|
}}
|
||||||
@@ -2841,16 +2846,16 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span className="form-hint">
|
<span className="form-hint">
|
||||||
留空则使用内置默认提示词。修改后立即生效,无需重启。可变的统计信息(触发次数、对话内容)会自动附加在用户消息里,无需在此填写。
|
当前显示内置默认提示词,可直接编辑修改。修改后立即生效,无需重启。可变的统计信息(触发次数、对话内容)会自动附加在用户消息里,无需在此填写。
|
||||||
</span>
|
</span>
|
||||||
<textarea
|
<textarea
|
||||||
className="field-input"
|
className="field-input"
|
||||||
rows={8}
|
rows={8}
|
||||||
style={{ width: '100%', resize: 'vertical', fontFamily: 'monospace', fontSize: 12 }}
|
style={{ width: '100%', resize: 'vertical', fontFamily: 'monospace', fontSize: 12 }}
|
||||||
placeholder={DEFAULT_SYSTEM_PROMPT}
|
value={displayValue}
|
||||||
value={aiInsightSystemPrompt}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const val = e.target.value
|
const val = e.target.value
|
||||||
|
// 如果用户把内容改得和默认值一样,仍存自定义值(不影响功能)
|
||||||
setAiInsightSystemPrompt(val)
|
setAiInsightSystemPrompt(val)
|
||||||
scheduleConfigSave('aiInsightSystemPrompt', () => configService.setAiInsightSystemPrompt(val))
|
scheduleConfigSave('aiInsightSystemPrompt', () => configService.setAiInsightSystemPrompt(val))
|
||||||
}}
|
}}
|
||||||
@@ -3117,7 +3122,7 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
|
|||||||
<p className="api-desc" style={{ lineHeight: 1.7 }}>
|
<p className="api-desc" style={{ lineHeight: 1.7 }}>
|
||||||
<strong>触发方式一:活跃会话分析</strong> — 每当微信数据库变化(即你收到新消息)时,经过 500ms 防抖后,对最近活跃的私聊会话进行分析。<br />
|
<strong>触发方式一:活跃会话分析</strong> — 每当微信数据库变化(即你收到新消息)时,经过 500ms 防抖后,对最近活跃的私聊会话进行分析。<br />
|
||||||
<strong>触发方式二:沉默扫描</strong> — 每 4 小时独立扫描一次,对超过阈值天数无消息的联系人发出提醒。<br />
|
<strong>触发方式二:沉默扫描</strong> — 每 4 小时独立扫描一次,对超过阈值天数无消息的联系人发出提醒。<br />
|
||||||
<strong>时间观念</strong> — 每次调用时<EFBFBD><EFBFBD>AI 会收到今天已向该联系人和全局发出过多少次见解,由 AI 自行决定是否需要克制。<br />
|
<strong>时间观念</strong> — 每次调用时,AI 会收到今天已向该联系人和全局发出过多少次见解,由 AI 自行决定是否需要克制。<br />
|
||||||
<strong>隐私</strong> — 所有分析请求均直接从你的电脑发往你填写的 API 地址,不经过任何 WeFlow 服务器。
|
<strong>隐私</strong> — 所有分析请求均直接从你的电脑发往你填写的 API 地址,不经过任何 WeFlow 服务器。
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user