From cff1705c5b21c4172c9b9f896b6c61198fac36af Mon Sep 17 00:00:00 2001 From: chekayo <9827969+chekayo@user.noreply.gitee.com> Date: Thu, 9 Oct 2025 23:49:40 +0800 Subject: [PATCH] feat: add reply comment functionality for Xiaohongshu feeds - Implemented handleReplyComment method in mcp_handlers.go to manage replying to comments on feeds. - Introduced ReplyCommentArgs struct in mcp_server.go for handling parameters related to comment replies. - Registered a new MCP tool for replying to comments in the registerTools function. - Added ReplyCommentToFeed method in service.go to interact with the Xiaohongshu platform for comment replies. - Enhanced error handling for missing parameters in the reply process. --- mcp_handlers.go | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ mcp_server.go | 34 ++++++++++++++++++-- service.go | 15 +++++++++ 3 files changed, 130 insertions(+), 3 deletions(-) diff --git a/mcp_handlers.go b/mcp_handlers.go index b2577d7..5bcd1fb 100644 --- a/mcp_handlers.go +++ b/mcp_handlers.go @@ -524,3 +524,87 @@ func (s *AppServer) handlePostComment(ctx context.Context, args map[string]inter }}, } } + +// handleReplyComment 处理回复评论 +func (s *AppServer) handleReplyComment(ctx context.Context, args map[string]interface{}) *MCPToolResult { + logrus.Info("MCP: 回复评论") + + // 解析参数 + feedID, ok := args["feed_id"].(string) + if !ok || feedID == "" { + return &MCPToolResult{ + Content: []MCPContent{{ + Type: "text", + Text: "回复评论失败: 缺少feed_id参数", + }}, + IsError: true, + } + } + + xsecToken, ok := args["xsec_token"].(string) + if !ok || xsecToken == "" { + return &MCPToolResult{ + Content: []MCPContent{{ + Type: "text", + Text: "回复评论失败: 缺少xsec_token参数", + }}, + IsError: true, + } + } + + commentID, ok := args["comment_id"].(string) + if !ok || commentID == "" { + return &MCPToolResult{ + Content: []MCPContent{{ + Type: "text", + Text: "回复评论失败: 缺少comment_id参数", + }}, + IsError: true, + } + } + + userID, ok := args["user_id"].(string) + if !ok || userID == "" { + return &MCPToolResult{ + Content: []MCPContent{{ + Type: "text", + Text: "回复评论失败: 缺少user_id参数", + }}, + IsError: true, + } + } + + content, ok := args["content"].(string) + if !ok || content == "" { + return &MCPToolResult{ + Content: []MCPContent{{ + Type: "text", + Text: "回复评论失败: 缺少content参数", + }}, + IsError: true, + } + } + + logrus.Infof("MCP: 回复评论 - Feed ID: %s, Comment ID: %s, User ID: %s, 内容长度: %d", feedID, commentID, userID, len(content)) + + // 回复评论 + result, err := s.xiaohongshuService.ReplyCommentToFeed(ctx, feedID, xsecToken, commentID, userID, content) + if err != nil { + return &MCPToolResult{ + Content: []MCPContent{{ + Type: "text", + Text: "回复评论失败: " + err.Error(), + }}, + IsError: true, + } + } + + // 返回成功结果 + resultText := fmt.Sprintf("回复评论成功 - Feed ID: %s", result.FeedID) + return &MCPToolResult{ + Content: []MCPContent{{ + Type: "text", + Text: resultText, + }}, + } +} diff --git a/mcp_server.go b/mcp_server.go index 3e1ce9e..81c9a2b 100644 --- a/mcp_server.go +++ b/mcp_server.go @@ -50,6 +50,15 @@ type PostCommentArgs struct { Content string `json:"content" jsonschema:"评论内容"` } +// ReplyCommentArgs 回复评论的参数 +type ReplyCommentArgs struct { + FeedID string `json:"feed_id" jsonschema:"小红书笔记ID,从Feed列表获取"` + XsecToken string `json:"xsec_token" jsonschema:"访问令牌,从Feed列表的xsecToken字段获取"` + CommentID string `json:"comment_id" jsonschema:"评论ID"` + UserID string `json:"user_id" jsonschema:"用户ID"` + Content string `json:"content" jsonschema:"回复内容"` +} + // LikeFeedArgs 点赞参数 type LikeFeedArgs struct { FeedID string `json:"feed_id" jsonschema:"小红书笔记ID,从Feed列表获取"` @@ -204,7 +213,26 @@ func registerTools(server *mcp.Server, appServer *AppServer) { }, ) - // 工具 9: 发布视频(仅本地文件) + // 工具 9: 回复评论 + mcp.AddTool(server, + &mcp.Tool{ + Name: "reply_to_comment", + Description: "回复小红书笔记的评论", + }, + func(ctx context.Context, req *mcp.CallToolRequest, args ReplyCommentArgs) (*mcp.CallToolResult, any, error) { + argsMap := map[string]interface{}{ + "feed_id": args.FeedID, + "xsec_token": args.XsecToken, + "comment_id": args.CommentID, + "user_id": args.UserID, + "content": args.Content, + } + result := appServer.handleReplyComment(ctx, argsMap) + return convertToMCPResult(result), nil, nil + }, + ) + + // 工具 10: 发布视频(仅本地文件) mcp.AddTool(server, &mcp.Tool{ Name: "publish_with_video", @@ -222,7 +250,7 @@ func registerTools(server *mcp.Server, appServer *AppServer) { }, ) - // 工具 10: 点赞笔记 + // 工具 11: 点赞笔记 mcp.AddTool(server, &mcp.Tool{ Name: "like_feed", @@ -239,7 +267,7 @@ func registerTools(server *mcp.Server, appServer *AppServer) { }, ) - // 工具 11: 收藏笔记 + // 工具 12: 收藏笔记 mcp.AddTool(server, &mcp.Tool{ Name: "favorite_feed", diff --git a/service.go b/service.go index 9cee00b..152adad 100644 --- a/service.go +++ b/service.go @@ -443,6 +443,21 @@ func (s *XiaohongshuService) UnfavoriteFeed(ctx context.Context, feedID, xsecTok return &ActionResult{FeedID: feedID, Success: true, Message: "取消收藏成功或未收藏"}, nil } +// ReplyCommentToFeed 回复笔记评论 +func (s *XiaohongshuService) ReplyCommentToFeed(ctx context.Context, feedID, xsecToken, commentID, userID, content string) (*ActionResult, error) { + b := newBrowser() + defer b.Close() + + page := b.NewPage() + defer page.Close() + + action := xiaohongshu.NewCommentFeedAction(page) + if err := action.ReplyToComment(ctx, feedID, xsecToken, commentID, userID, content); err != nil { + return nil, err + } + return &ActionResult{FeedID: feedID, Success: true, Message: "回复评论成功"}, nil +} + func newBrowser() *headless_browser.Browser { return browser.NewBrowser(configs.IsHeadless(), browser.WithBinPath(configs.GetBinPath())) }