feat(diary): Enhance Diary
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user