Skip to content

Commit 13ac2e3

Browse files
haikowchekayo
andauthored
修复完善笔记详情内容加载 (#301)
* feat: add like and favorite functionality for feeds - Implemented handleLikeFeed and handleFavoriteFeed methods in mcp_handlers.go to manage liking and favoriting feeds. - Added LikeFavoriteArgs struct in mcp_server.go for handling parameters. - Registered new MCP tools for liking and favoriting feeds in registerTools function. - Introduced LikeFeed and FavoriteFeed methods in XiaohongshuService to interact with the respective actions. - Created LikeFavoriteAction in a new file to encapsulate the logic for liking and favoriting feeds on the Xiaohongshu platform. * "优化评论反馈逻辑:简化回复按钮查找和点击流程 * "Fix-build-errors" * refactor: streamline like and favorite actions in LikeFavoriteAction - Introduced a generic method `performInteractAction` to handle both liking and favoriting feeds, reducing code duplication. - Updated logging to reflect the action type being performed (like or favorite). - Enhanced state verification after interaction to ensure accurate feedback on success or failure. - Removed the `clickLastMatch` function, simplifying the interaction logic. * "Add-unlike-and-unfavorite-functionality" * "Refactor-performInteractAction-function" * "Refactor-split-LikeFavoriteAction-into-separate-actions" * "Add-content-length-validation-for-publish" * refactor: improve comment posting logic with enhanced error handling and stability checks - Updated the PostComment method to include error handling for navigation and element interactions. - Replaced sleep calls with more reliable wait mechanisms to ensure page stability. - Added checks for the presence of input elements and improved logging for better debugging. * 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. * refactor: enhance reply comment functionality with improved error handling and response structure - Simplified error handling in handleReplyComment to check for both comment_id and user_id simultaneously. - Updated response message to include both Comment ID and User ID upon successful reply. - Modified ReplyCommentArgs struct to make comment_id and user_id optional. - Renamed MCP tool for replying to comments for clarity. * feat(feed): Migrate loadAllComments feature for GetFeedDetail * fix * fix * fix * fix * fix: 添加更多自定义选项操作 * fix * fix:优化代码结构 * chore: update dependencies and implement retry logic for page interactions --------- Co-authored-by: chekayo <9827969+chekayo@user.noreply.gitee.com>
1 parent 6bef535 commit 13ac2e3

File tree

8 files changed

+890
-40
lines changed

8 files changed

+890
-40
lines changed

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ require (
1010
github.com/modelcontextprotocol/go-sdk v0.7.0
1111
github.com/pkg/errors v0.9.1
1212
github.com/sirupsen/logrus v1.9.3
13-
github.com/stretchr/testify v1.10.0
13+
github.com/stretchr/testify v1.11.1
1414
github.com/xpzouying/headless_browser v0.2.0
1515
)
1616

1717
require (
18+
github.com/avast/retry-go/v4 v4.7.0 // indirect
1819
github.com/bytedance/sonic v1.11.6 // indirect
1920
github.com/bytedance/sonic/loader v0.1.1 // indirect
2021
github.com/cloudwego/base64x v0.1.4 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/avast/retry-go/v4 v4.7.0 h1:yjDs35SlGvKwRNSykujfjdMxMhMQQM0TnIjJaHB+Zio=
2+
github.com/avast/retry-go/v4 v4.7.0/go.mod h1:ZMPDa3sY2bKgpLtap9JRUgk2yTAba7cgiFhqxY2Sg6Q=
13
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
24
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
35
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
@@ -79,6 +81,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
7981
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
8082
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
8183
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
84+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
85+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
8286
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
8387
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
8488
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=

handlers_api.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,23 @@ func (s *AppServer) getFeedDetailHandler(c *gin.Context) {
181181
return
182182
}
183183

184-
// 获取 Feed 详情
185-
result, err := s.xiaohongshuService.GetFeedDetail(c.Request.Context(), req.FeedID, req.XsecToken)
184+
var result *FeedDetailResponse
185+
var err error
186+
187+
if req.CommentConfig != nil {
188+
// 使用配置参数
189+
config := xiaohongshu.CommentLoadConfig{
190+
ClickMoreReplies: req.CommentConfig.ClickMoreReplies,
191+
MaxRepliesThreshold: req.CommentConfig.MaxRepliesThreshold,
192+
MaxCommentItems: req.CommentConfig.MaxCommentItems,
193+
ScrollSpeed: req.CommentConfig.ScrollSpeed,
194+
}
195+
result, err = s.xiaohongshuService.GetFeedDetailWithConfig(c.Request.Context(), req.FeedID, req.XsecToken, req.LoadAllComments, config)
196+
} else {
197+
// 使用默认配置
198+
result, err = s.xiaohongshuService.GetFeedDetail(c.Request.Context(), req.FeedID, req.XsecToken, req.LoadAllComments)
199+
}
200+
186201
if err != nil {
187202
respondError(c, http.StatusInternalServerError, "GET_FEED_DETAIL_FAILED",
188203
"获取Feed详情失败", err.Error())

mcp_handlers.go

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"strconv"
8+
"strings"
9+
"time"
10+
711
"github.com/sirupsen/logrus"
812
"github.com/xpzouying/xiaohongshu-mcp/cookies"
913
"github.com/xpzouying/xiaohongshu-mcp/xiaohongshu"
10-
"strings"
11-
"time"
1214
)
1315

1416
// MCP 工具处理函数
@@ -35,7 +37,7 @@ func (s *AppServer) handleCheckLoginStatus(ctx context.Context) *MCPToolResult {
3537
} else {
3638
resultText = fmt.Sprintf("❌ 未登录\n\n请使用 get_login_qrcode 工具获取二维码进行登录。")
3739
}
38-
40+
3941
return &MCPToolResult{
4042
Content: []MCPContent{{
4143
Type: "text",
@@ -336,9 +338,67 @@ func (s *AppServer) handleGetFeedDetail(ctx context.Context, args map[string]any
336338
}
337339
}
338340

339-
logrus.Infof("MCP: 获取Feed详情 - Feed ID: %s", feedID)
341+
loadAll := false
342+
if raw, ok := args["load_all_comments"]; ok {
343+
switch v := raw.(type) {
344+
case bool:
345+
loadAll = v
346+
case string:
347+
if parsed, err := strconv.ParseBool(v); err == nil {
348+
loadAll = parsed
349+
}
350+
case float64:
351+
loadAll = v != 0
352+
}
353+
}
354+
355+
// 解析评论配置参数,如果未提供则使用默认值
356+
config := xiaohongshu.DefaultCommentLoadConfig()
357+
358+
if raw, ok := args["click_more_replies"]; ok {
359+
switch v := raw.(type) {
360+
case bool:
361+
config.ClickMoreReplies = v
362+
case string:
363+
if parsed, err := strconv.ParseBool(v); err == nil {
364+
config.ClickMoreReplies = parsed
365+
}
366+
}
367+
}
368+
369+
if raw, ok := args["max_replies_threshold"]; ok {
370+
switch v := raw.(type) {
371+
case float64:
372+
config.MaxRepliesThreshold = int(v)
373+
case string:
374+
if parsed, err := strconv.Atoi(v); err == nil {
375+
config.MaxRepliesThreshold = parsed
376+
}
377+
case int:
378+
config.MaxRepliesThreshold = v
379+
}
380+
}
381+
382+
if raw, ok := args["max_comment_items"]; ok {
383+
switch v := raw.(type) {
384+
case float64:
385+
config.MaxCommentItems = int(v)
386+
case string:
387+
if parsed, err := strconv.Atoi(v); err == nil {
388+
config.MaxCommentItems = parsed
389+
}
390+
case int:
391+
config.MaxCommentItems = v
392+
}
393+
}
394+
395+
if raw, ok := args["scroll_speed"].(string); ok && raw != "" {
396+
config.ScrollSpeed = raw
397+
}
398+
399+
logrus.Infof("MCP: 获取Feed详情 - Feed ID: %s, loadAllComments=%v, config=%+v", feedID, loadAll, config)
340400

341-
result, err := s.xiaohongshuService.GetFeedDetail(ctx, feedID, xsecToken)
401+
result, err := s.xiaohongshuService.GetFeedDetailWithConfig(ctx, feedID, xsecToken, loadAll, config)
342402
if err != nil {
343403
return &MCPToolResult{
344404
Content: []MCPContent{{

mcp_server.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,13 @@ type FilterOption struct {
4545

4646
// FeedDetailArgs 获取Feed详情的参数
4747
type FeedDetailArgs struct {
48-
FeedID string `json:"feed_id" jsonschema:"小红书笔记ID,从Feed列表获取"`
49-
XsecToken string `json:"xsec_token" jsonschema:"访问令牌,从Feed列表的xsecToken字段获取"`
48+
FeedID string `json:"feed_id" jsonschema:"小红书笔记ID,从Feed列表获取"`
49+
XsecToken string `json:"xsec_token" jsonschema:"访问令牌,从Feed列表的xsecToken字段获取"`
50+
LoadAllComments bool `json:"load_all_comments,omitempty" jsonschema:"是否加载全部评论(默认false,仅返回首批评论)"`
51+
ClickMoreReplies bool `json:"click_more_replies,omitempty" jsonschema:"是否点击'更多回复'按钮 (默认: false)"`
52+
MaxRepliesThreshold int `json:"max_replies_threshold,omitempty" jsonschema:"回复数量阈值,超过此数量的'更多'按钮将被跳过 (0表示不跳过任何, 默认: 10)"`
53+
MaxCommentItems int `json:"max_comment_items,omitempty" jsonschema:"最大加载评论数(0表示加载所有, 默认: 0)"`
54+
ScrollSpeed string `json:"scroll_speed,omitempty" jsonschema:"滚动速度: 'slow'|'normal'|'fast' (默认: 'normal')"`
5055
}
5156

5257
// UserProfileArgs 获取用户主页的参数
@@ -216,8 +221,13 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
216221
},
217222
withPanicRecovery("get_feed_detail", func(ctx context.Context, req *mcp.CallToolRequest, args FeedDetailArgs) (*mcp.CallToolResult, any, error) {
218223
argsMap := map[string]interface{}{
219-
"feed_id": args.FeedID,
220-
"xsec_token": args.XsecToken,
224+
"feed_id": args.FeedID,
225+
"xsec_token": args.XsecToken,
226+
"load_all_comments": args.LoadAllComments,
227+
"click_more_replies": args.ClickMoreReplies,
228+
"max_replies_threshold": args.MaxRepliesThreshold,
229+
"max_comment_items": args.MaxCommentItems,
230+
"scroll_speed": args.ScrollSpeed,
221231
}
222232
result := appServer.handleGetFeedDetail(ctx, argsMap)
223233
return convertToMCPResult(result), nil, nil

service.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,12 @@ func (s *XiaohongshuService) SearchFeeds(ctx context.Context, keyword string, fi
328328
}
329329

330330
// GetFeedDetail 获取Feed详情
331-
func (s *XiaohongshuService) GetFeedDetail(ctx context.Context, feedID, xsecToken string) (*FeedDetailResponse, error) {
331+
func (s *XiaohongshuService) GetFeedDetail(ctx context.Context, feedID, xsecToken string, loadAllComments bool) (*FeedDetailResponse, error) {
332+
return s.GetFeedDetailWithConfig(ctx, feedID, xsecToken, loadAllComments, xiaohongshu.DefaultCommentLoadConfig())
333+
}
334+
335+
// GetFeedDetailWithConfig 使用配置获取Feed详情
336+
func (s *XiaohongshuService) GetFeedDetailWithConfig(ctx context.Context, feedID, xsecToken string, loadAllComments bool, config xiaohongshu.CommentLoadConfig) (*FeedDetailResponse, error) {
332337
b := newBrowser()
333338
defer b.Close()
334339

@@ -339,7 +344,7 @@ func (s *XiaohongshuService) GetFeedDetail(ctx context.Context, feedID, xsecToke
339344
action := xiaohongshu.NewFeedDetailAction(page)
340345

341346
// 获取 Feed 详情
342-
result, err := action.GetFeedDetail(ctx, feedID, xsecToken)
347+
result, err := action.GetFeedDetailWithConfig(ctx, feedID, xsecToken, loadAllComments, config)
343348
if err != nil {
344349
return nil, err
345350
}

types.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,24 @@ type MCPContent struct {
3434
Data string `json:"data"`
3535
}
3636

37+
// CommentLoadConfig 评论加载配置
38+
type CommentLoadConfig struct {
39+
// 是否点击"更多回复"按钮
40+
ClickMoreReplies bool `json:"click_more_replies,omitempty"`
41+
// 回复数量阈值,超过这个数量的"更多"按钮将被跳过(0表示不跳过任何)
42+
MaxRepliesThreshold int `json:"max_replies_threshold,omitempty"`
43+
// 最大加载评论数(comment-item数量),0表示加载所有
44+
MaxCommentItems int `json:"max_comment_items,omitempty"`
45+
// 滚动速度等级: slow(慢速), normal(正常), fast(快速)
46+
ScrollSpeed string `json:"scroll_speed,omitempty"`
47+
}
48+
3749
// FeedDetailRequest Feed详情请求
3850
type FeedDetailRequest struct {
39-
FeedID string `json:"feed_id" binding:"required"`
40-
XsecToken string `json:"xsec_token" binding:"required"`
51+
FeedID string `json:"feed_id" binding:"required"`
52+
XsecToken string `json:"xsec_token" binding:"required"`
53+
LoadAllComments bool `json:"load_all_comments,omitempty"`
54+
CommentConfig *CommentLoadConfig `json:"comment_config,omitempty"`
4155
}
4256

4357
type SearchFeedsRequest struct {

0 commit comments

Comments
 (0)