refactor: Private bool → Visibility string 支持多种可见范围 (#464)

* docs: 更新 API 文档以包含 private 参数的用途和可选性。

* refactor: visibility 功能从 Private bool 重构为 Visibility string

将发布时可见范围参数从 `Private bool` 改为 `Visibility string`,
支持三种选项:公开可见(默认)、仅自己可见、仅互关好友可见。

- 使用精确 CSS selector 替代遍历 span/label/div 的宽泛选择器
- 新增参数校验,不支持的选项直接返回错误
- 更新 API 文档和 MCP jsonschema 描述
- 与 upstream IsOriginal(原创声明) 功能共存

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: yryangang <dd101bb@qq.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zy
2026-02-28 00:37:47 +08:00
committed by GitHub
parent 7d87b9e5ee
commit fcbf554016
6 changed files with 95 additions and 9 deletions

View File

@@ -23,6 +23,7 @@ type PublishImageContent struct {
ImagePaths []string
ScheduleTime *time.Time // 定时发布时间nil 表示立即发布
IsOriginal bool // 是否声明原创
Visibility string // 可见范围: "公开可见"(默认), "仅自己可见", "仅互关好友可见"
}
type PublishAction struct {
@@ -83,9 +84,9 @@ func (p *PublishAction) Publish(ctx context.Context, content PublishImageContent
tags = tags[:10]
}
logrus.Infof("发布内容: title=%s, images=%v, tags=%v, schedule=%v, original=%v", content.Title, len(content.ImagePaths), tags, content.ScheduleTime, content.IsOriginal)
logrus.Infof("发布内容: title=%s, images=%v, tags=%v, schedule=%v, original=%v, visibility=%s", content.Title, len(content.ImagePaths), tags, content.ScheduleTime, content.IsOriginal, content.Visibility)
if err := submitPublish(page, content.Title, content.Content, tags, content.ScheduleTime, content.IsOriginal); err != nil {
if err := submitPublish(page, content.Title, content.Content, tags, content.ScheduleTime, content.IsOriginal, content.Visibility); err != nil {
return errors.Wrap(err, "小红书发布失败")
}
@@ -269,7 +270,7 @@ func waitForUploadComplete(page *rod.Page, expectedCount int) error {
return errors.Errorf("第%d张图片上传超时(60s),请检查网络连接和图片大小", expectedCount)
}
func submitPublish(page *rod.Page, title, content string, tags []string, scheduleTime *time.Time, isOriginal bool) error {
func submitPublish(page *rod.Page, title, content string, tags []string, scheduleTime *time.Time, isOriginal bool, visibility string) error {
titleElem, err := page.Element("div.d-input input")
if err != nil {
return errors.Wrap(err, "查找标题输入框失败")
@@ -314,6 +315,11 @@ func submitPublish(page *rod.Page, title, content string, tags []string, schedul
slog.Info("定时发布设置完成", "schedule_time", scheduleTime.Format("2006-01-02 15:04"))
}
// 设置可见范围
if err := setVisibility(page, visibility); err != nil {
return errors.Wrap(err, "设置可见范围失败")
}
// 处理原创声明
if isOriginal {
if err := setOriginal(page); err != nil {
@@ -572,6 +578,52 @@ func isElementVisible(elem *rod.Element) bool {
return visible
}
// setVisibility 设置可见范围
// 支持: "公开可见"(默认), "仅自己可见", "仅互关好友可见"
func setVisibility(page *rod.Page, visibility string) error {
if visibility == "" || visibility == "公开可见" {
slog.Info("可见范围使用默认:公开可见")
return nil
}
// 支持的选项校验
supported := map[string]bool{"仅自己可见": true, "仅互关好友可见": true}
if !supported[visibility] {
return errors.Errorf("不支持的可见范围: %s支持: 公开可见、仅自己可见、仅互关好友可见", visibility)
}
// 点击可见范围下拉框
dropdown, err := page.Element("div.permission-card-wrapper div.d-select-content")
if err != nil {
return errors.Wrap(err, "查找可见范围下拉框失败")
}
if err := dropdown.Click(proto.InputMouseButtonLeft, 1); err != nil {
return errors.Wrap(err, "点击可见范围下拉框失败")
}
time.Sleep(500 * time.Millisecond)
// 在弹窗中查找并点击目标选项
opts, err := page.Elements("div.d-options-wrapper div.d-grid-item div.custom-option")
if err != nil {
return errors.Wrap(err, "查找可见范围选项失败")
}
for _, opt := range opts {
text, err := opt.Text()
if err != nil {
continue
}
if strings.Contains(text, visibility) {
if err := opt.Click(proto.InputMouseButtonLeft, 1); err != nil {
return errors.Wrap(err, "选择可见范围失败")
}
slog.Info("已设置可见范围", "visibility", visibility)
time.Sleep(200 * time.Millisecond)
return nil
}
}
return errors.Errorf("未找到可见范围选项: %s", visibility)
}
// setSchedulePublish 设置定时发布时间
func setSchedulePublish(page *rod.Page, t time.Time) error {
// 1. 点击定时发布开关

View File

@@ -20,6 +20,7 @@ type PublishVideoContent struct {
Tags []string
VideoPath string
ScheduleTime *time.Time // 定时发布时间nil 表示立即发布
Visibility string // 可见范围: "公开可见"(默认), "仅自己可见", "仅互关好友可见"
}
// NewPublishVideoAction 进入发布页并切换到"上传视频"
@@ -62,7 +63,7 @@ func (p *PublishAction) PublishVideo(ctx context.Context, content PublishVideoCo
return errors.Wrap(err, "小红书上传视频失败")
}
if err := submitPublishVideo(page, content.Title, content.Content, content.Tags, content.ScheduleTime); err != nil {
if err := submitPublishVideo(page, content.Title, content.Content, content.Tags, content.ScheduleTime, content.Visibility); err != nil {
return errors.Wrap(err, "小红书发布失败")
}
return nil
@@ -130,7 +131,7 @@ func waitForPublishButtonClickable(page *rod.Page) (*rod.Element, error) {
}
// submitPublishVideo 填写标题、正文、标签并点击发布(等待按钮可点击后再提交)
func submitPublishVideo(page *rod.Page, title, content string, tags []string, scheduleTime *time.Time) error {
func submitPublishVideo(page *rod.Page, title, content string, tags []string, scheduleTime *time.Time, visibility string) error {
// 标题
titleElem, err := page.Element("div.d-input input")
if err != nil {
@@ -163,6 +164,11 @@ func submitPublishVideo(page *rod.Page, title, content string, tags []string, sc
slog.Info("定时发布设置完成", "schedule_time", scheduleTime.Format("2006-01-02 15:04"))
}
// 设置可见范围
if err := setVisibility(page, visibility); err != nil {
return errors.Wrap(err, "设置可见范围失败")
}
// 等待发布按钮可点击
btn, err := waitForPublishButtonClickable(page)
if err != nil {