diff --git a/mcp_handlers.go b/mcp_handlers.go index a9d5a07..867913c 100644 --- a/mcp_handlers.go +++ b/mcp_handlers.go @@ -42,6 +42,7 @@ func (s *AppServer) handlePublishContent(ctx context.Context, args map[string]in title, _ := args["title"].(string) content, _ := args["content"].(string) imagePathsInterface, _ := args["images"].([]interface{}) + tagsInterface, _ := args["tags"].([]interface{}) var imagePaths []string for _, path := range imagePathsInterface { @@ -50,13 +51,21 @@ func (s *AppServer) handlePublishContent(ctx context.Context, args map[string]in } } - logrus.Infof("MCP: 发布内容 - 标题: %s, 图片数量: %d", title, len(imagePaths)) + var tags []string + for _, tag := range tagsInterface { + if tagStr, ok := tag.(string); ok { + tags = append(tags, tagStr) + } + } + + logrus.Infof("MCP: 发布内容 - 标题: %s, 图片数量: %d, 标签数量: %d", title, len(imagePaths), len(tags)) // 构建发布请求 req := &PublishRequest{ Title: title, Content: content, Images: imagePaths, + Tags: tags, } // 执行发布 diff --git a/service.go b/service.go index f97737a..d55969a 100644 --- a/service.go +++ b/service.go @@ -24,6 +24,7 @@ type PublishRequest struct { Title string `json:"title" binding:"required"` Content string `json:"content" binding:"required"` Images []string `json:"images" binding:"required,min=1"` + Tags []string `json:"tags,omitempty"` } // LoginStatusResponse 登录状态响应 @@ -89,6 +90,7 @@ func (s *XiaohongshuService) PublishContent(ctx context.Context, req *PublishReq content := xiaohongshu.PublishImageContent{ Title: req.Title, Content: req.Content, + Tags: req.Tags, ImagePaths: imagePaths, } diff --git a/streamable_http.go b/streamable_http.go index c1ea893..c678467 100644 --- a/streamable_http.go +++ b/streamable_http.go @@ -176,7 +176,7 @@ func (s *AppServer) processToolsList(request *JSONRPCRequest) *JSONRPCResponse { }, "content": map[string]interface{}{ "type": "string", - "description": "正文内容,支持话题标签", + "description": "正文内容", }, "images": map[string]interface{}{ "type": "array", @@ -186,6 +186,13 @@ func (s *AppServer) processToolsList(request *JSONRPCRequest) *JSONRPCResponse { }, "minItems": 1, }, + "tags": map[string]interface{}{ + "type": "array", + "description": "话题标签列表(可选),如 [\"美食\", \"旅行\", \"生活\"]", + "items": map[string]interface{}{ + "type": "string", + }, + }, }, "required": []string{"title", "content", "images"}, }, diff --git a/xiaohongshu/publish.go b/xiaohongshu/publish.go index 87b0863..c9af2f9 100644 --- a/xiaohongshu/publish.go +++ b/xiaohongshu/publish.go @@ -15,6 +15,7 @@ import ( type PublishImageContent struct { Title string Content string + Tags []string ImagePaths []string } @@ -74,7 +75,7 @@ func (p *PublishAction) Publish(ctx context.Context, content PublishImageContent return errors.Wrap(err, "小红书上传图片失败") } - if err := submitPublish(page, content.Title, content.Content); err != nil { + if err := submitPublish(page, content.Title, content.Content, content.Tags); err != nil { return errors.Wrap(err, "小红书发布失败") } @@ -96,7 +97,7 @@ func uploadImages(page *rod.Page, imagesPaths []string) error { return nil } -func submitPublish(page *rod.Page, title, content string) error { +func submitPublish(page *rod.Page, title, content string, tags []string) error { titleElem := page.MustElement("div.d-input input") titleElem.MustInput(title) @@ -105,12 +106,13 @@ func submitPublish(page *rod.Page, title, content string) error { if contentElem, ok := getContentElement(page); ok { contentElem.MustInput(content) + + inputTags(contentElem, tags) + } else { return errors.New("没有找到内容输入框") } - time.Sleep(1 * time.Second) - submitButton := page.MustElement("div.submit div.d-button-content") submitButton.MustClick() @@ -145,6 +147,54 @@ func getContentElement(page *rod.Page) (*rod.Element, bool) { return nil, false } +func inputTags(contentElem *rod.Element, tags []string) { + if len(tags) == 0 { + return + } + + contentElem.MustInput("\n\n") + + for _, tag := range tags { + + tag = strings.TrimLeft(tag, "#") + inputTag(contentElem, tag) + } +} + +func inputTag(contentElem *rod.Element, tag string) { + contentElem.MustInput("#") + time.Sleep(200 * time.Millisecond) // 等待标签系统激活 + + for _, char := range tag { + contentElem.MustInput(string(char)) + time.Sleep(50 * time.Millisecond) // 减少延迟时间 + } + + // 等待标签联想下拉框出现 + time.Sleep(500 * time.Millisecond) + + page := contentElem.Page() + topicContainer, err := page.Element("#creator-editor-topic-container") + if err == nil && topicContainer != nil { + firstItem, err := topicContainer.Element(".item") + if err == nil && firstItem != nil { + firstItem.MustClick() + slog.Info("成功点击标签联想选项", "tag", tag) + time.Sleep(200 * time.Millisecond) // 等待选择生效 + } else { + slog.Warn("未找到标签联想选项,直接输入空格", "tag", tag) + // 如果没有找到联想选项,输入空格结束 + contentElem.MustInput(" ") + } + } else { + slog.Warn("未找到标签联想下拉框,直接输入空格", "tag", tag) + // 如果没有找到下拉框,输入空格结束 + contentElem.MustInput(" ") + } + + time.Sleep(500 * time.Millisecond) // 等待标签处理完成 +} + func findTextboxByPlaceholder(page *rod.Page) (*rod.Element, error) { elements := page.MustElements("p") if elements == nil {