feat(diary): Enhance Diary

This commit is contained in:
Symb0x76
2025-09-15 19:56:45 +08:00
parent f75e1c3318
commit 594b50d616
3 changed files with 84 additions and 20 deletions
+31 -11
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"strconv"
"strings"
"time"
@@ -25,7 +26,6 @@ func (s *Service) initMCPServer() {
s.mcpServer.AddTool(RecentChatTool, s.handleMCPRecentChat)
s.mcpServer.AddTool(ChatLogTool, s.handleMCPChatLog)
s.mcpServer.AddTool(CurrentTimeTool, s.handleMCPCurrentTime)
// 日记(最近24h我说过话的会话全量消息)
s.mcpServer.AddTool(DiaryTool, s.handleMCPDiary)
s.mcpSSEServer = server.NewSSEServer(s.mcpServer)
s.mcpStreamableServer = server.NewStreamableHTTPServer(s.mcpServer)
@@ -157,24 +157,29 @@ var CurrentTimeTool = mcp.NewTool(
注意:此工具不需要任何输入参数,直接调用即可获取当前时间。`),
)
// DiaryTool: 最近24小时内“我”参与(至少有一条我发送的消息)的所有会话的全部消息
var DiaryTool = mcp.NewTool(
"query_diary",
mcp.WithDescription(`查询最近24小时内用户参与过(至少有一条由“我”发送)的所有会话的全部消息,并按会话分组返回。适用于:
mcp.WithDescription(`查询最近N小时内(可选:24/48/72)用户参与过(至少有一条由“我”发送)的所有会话的全部消息,并按会话分组返回。适用于:
1. 让助手总结我一天聊了什么
2. 统计我今天在不同群/联系人中的发言
3. 回顾我参与过的讨论上下文
无任何参数。返回格式:
参数:
- hours: 时间范围小时数,可选 24、48、72,默认 24
- talker: 可选,筛选特定会话(支持多个,用","分隔,ID/备注/昵称均可)
返回格式:
若存在会话:
[会话显示名(UserName)] - N条
2025-04-18 09:30:05 我 内容
2025-04-18 09:30:15 张三 内容
...
-----------------------------
若无结果:输出"最近24小时没有我参与的会话"。
若无结果:输出"最近Nh没有我参与的会话"。
注意:此工具返回的是完整原始消息内容(已做必要的占位符文本化),不做额外摘要。若需要摘要,请先调用本工具获取原始数据后再进行总结。`),
mcp.WithString("hours", mcp.Description("时间范围小时数,可选:24、48、72;默认为24")),
mcp.WithString("talker", mcp.Description("可选,会话筛选(多个用','分隔)")),
)
type ContactRequest struct {
@@ -350,12 +355,27 @@ func (s *Service) handleMCPCurrentTime(ctx context.Context, request mcp.CallTool
}, nil
}
// handleMCPDiary 实现 DiaryTool 逻辑
func (s *Service) handleMCPDiary(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
end := time.Now()
start := end.Add(-24 * time.Hour)
type DiaryRequest struct {
Hours string `json:"hours"`
Talker string `json:"talker"`
}
sessionsResp, err := s.db.GetSessions("", 0, 0)
// handleMCPDiary 实现 DiaryTool 逻辑(支持 hours 与 talker)
func (s *Service) handleMCPDiary(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
var req DiaryRequest
_ = request.BindArguments(&req) // 可选参数,忽略绑定错误
hours := 24
if strings.TrimSpace(req.Hours) != "" {
if v, err := strconv.Atoi(req.Hours); err == nil && (v == 24 || v == 48 || v == 72) {
hours = v
}
}
end := time.Now()
start := end.Add(-time.Duration(hours) * time.Hour)
sessionsResp, err := s.db.GetSessions(req.Talker, 0, 0)
if err != nil {
return &mcp.CallToolResult{Content: []mcp.Content{mcp.TextContent{Type: "text", Text: "获取会话失败: " + err.Error()}}}, nil
}
@@ -378,7 +398,7 @@ func (s *Service) handleMCPDiary(ctx context.Context, request mcp.CallToolReques
buf := &bytes.Buffer{}
if len(groups) == 0 {
buf.WriteString("最近24小时没有我参与的会话")
buf.WriteString(fmt.Sprintf("最近%dh没有我参与的会话", hours))
} else {
for _, g := range groups {
header := g.Talker
+16 -8
View File
@@ -373,16 +373,24 @@ func (s *Service) handleSessions(c *gin.Context) {
}
}
// handleDiary 返回最近24小时内“我”发送的消息,按 talker 分组。
// GET /api/v1/diary?format=(html|json|csv|text)
// handleDiary 返回最近N(24/48/72)小时内“我”发送的消息,按 talker 分组。
// GET /api/v1/diary?hours=(24|48|72)&format=(html|json|csv|text)
func (s *Service) handleDiary(c *gin.Context) {
q := struct { Format string `form:"format"` }{}
q := struct {
Hours int `form:"hours"`
Talker string `form:"talker"`
Format string `form:"format"`
}{}
if err := c.BindQuery(&q); err != nil { errors.Err(c, err); return }
// 默认24h,仅允许 24/48/72
hours := q.Hours
if hours == 0 { hours = 24 }
if hours != 24 && hours != 48 && hours != 72 { hours = 24 }
end := time.Now()
start := end.Add(-24 * time.Hour)
start := end.Add(-time.Duration(hours) * time.Hour)
// 获取所有会话
sessionsResp, err := s.db.GetSessions("", 0, 0)
// 获取会话(可选 talker 过滤)
sessionsResp, err := s.db.GetSessions(q.Talker, 0, 0)
if err != nil { errors.Err(c, err); return }
type grouped struct {
@@ -405,8 +413,8 @@ func (s *Service) handleDiary(c *gin.Context) {
switch format {
case "html":
c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
c.Writer.WriteString(`<html><head><meta charset="utf-8"><title>Diary - 24h</title><style>body{font-family:Arial,Helvetica,sans-serif;font-size:14px;}details{margin:8px 0;padding:6px 8px;border:1px solid #ddd;border-radius:6px;background:#fafafa;}summary{cursor:pointer;font-weight:600;} .msg{margin:4px 0;padding:4px 6px;border-left:3px solid #2ecc71;background:#fff;} .meta{color:#666;font-size:12px;margin-bottom:2px;} pre{white-space:pre-wrap;word-break:break-word;margin:0;} .sender{color:#27ae60;} .time{color:#16a085;margin-left:6px;} a.media{color:#2c3e50;text-decoration:none;} a.media:hover{text-decoration:underline;}</style></head><body>`)
c.Writer.WriteString(fmt.Sprintf("<h2>最近24小时我参与过的会话全部消息(%s ~ %s)</h2>", start.Format("2006-01-02 15:04:05"), end.Format("2006-01-02 15:04:05")))
c.Writer.WriteString(`<html><head><meta charset="utf-8"><title>Diary</title><style>body{font-family:Arial,Helvetica,sans-serif;font-size:14px;}details{margin:8px 0;padding:6px 8px;border:1px solid #ddd;border-radius:6px;background:#fafafa;}summary{cursor:pointer;font-weight:600;} .msg{margin:4px 0;padding:4px 6px;border-left:3px solid #2ecc71;background:#fff;} .meta{color:#666;font-size:12px;margin-bottom:2px;} pre{white-space:pre-wrap;word-break:break-word;margin:0;} .sender{color:#27ae60;} .time{color:#16a085;margin-left:6px;} a.media{color:#2c3e50;text-decoration:none;} a.media:hover{text-decoration:underline;}</style></head><body>`)
c.Writer.WriteString(fmt.Sprintf("<h2>最近%dh我参与过的会话全部消息(%s ~ %s)</h2>", hours, start.Format("2006-01-02 15:04:05"), end.Format("2006-01-02 15:04:05")))
for _, g := range groups {
title := g.Talker
if g.TalkerName != "" { title = fmt.Sprintf("%s (%s)", g.TalkerName, g.Talker) }
+37 -1
View File
@@ -381,12 +381,36 @@
<div class="tab-content" id="diary-tab">
<div class="api-description">
<p>
查询最近24小时我参与过的会话全部消息。<span
查询最近N小时我参与过的会话全部消息(可选:24h、48h、72h)。<span
class="badge"
>GET /api/v1/diary</span
>
</p>
</div>
<div class="form-group">
<label for="diary-hours"
>时间范围:<span class="optional-param"
>可选</span
></label
>
<select id="diary-hours">
<option value="24" selected>24h</option>
<option value="48">48h</option>
<option value="72">72h</option>
</select>
</div>
<div class="form-group">
<label for="diary-talker"
>聊天对象:<span class="optional-param"
>可选</span
></label
>
<input
type="text"
id="diary-talker"
placeholder="wxid、群ID、备注名或昵称(多个用逗号分隔)"
/>
</div>
<div class="form-group">
<label for="diary-format"
>输出格式:<span class="optional-param"
@@ -785,10 +809,22 @@
break;
case "diary":
url += "diary";
const diaryHours =
document.getElementById(
"diary-hours"
).value;
const diaryTalker =
document.getElementById(
"diary-talker"
).value;
const diaryFormat =
document.getElementById(
"diary-format"
).value;
if (diaryHours)
params.append("hours", diaryHours);
if (diaryTalker)
params.append("talker", diaryTalker);
if (diaryFormat)
params.append("format", diaryFormat);
break;