package collect import ( "context" "fmt" "geo/internal/config" "log" "strings" "time" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" ) // DeepseekCollector DeepSeek收集器 type DeepseekCollector struct { *BaseCollector } // NewDeepseekCollector 创建DeepSeek收集器 func NewDeepseekCollector(ctx context.Context, params *CollectParams, cfg *config.Config, logger *log.Logger) CollectorInterface { collector := &DeepseekCollector{ BaseCollector: NewBaseCollector(ctx, params, cfg, logger), } // 设置DeepSeek的URL collector.LoginURL = "https://chat.deepseek.com/" collector.ChatURL = "https://chat.deepseek.com/" return collector } // CheckLoginStatus 检查登录状态 func (c *DeepseekCollector) CheckLoginStatus() bool { currentURL := c.GetCurrentURL() // 如果在首页或登录页面,可能未登录 if strings.Contains(currentURL, "chat.deepseek.com") { // 检查是否有用户头像或登录标识 userAvatar, err := c.SafeElement(".user-avatar, [class*='avatar'], [class*='profile']") if err == nil && userAvatar != nil { return true } // 检查是否有聊天输入框(登录后才有) inputBox, err := c.SafeElement("textarea, [contenteditable='true']") if err == nil && inputBox != nil { return true } } return false } // WaitLogin 等待登录 func (c *DeepseekCollector) WaitLogin() (bool, string) { c.LogInfo("开始等待DeepSeek登录...") if err := c.SetupDriver(); err != nil { return false, fmt.Sprintf("浏览器启动失败: %v", err) } defer c.Close() // 访问聊天页面 c.Page.MustNavigate(c.ChatURL) c.Sleep(3) // 检查是否已登录 if c.CheckLoginStatus() { c.SaveCookies() c.LogInfo("已有登录状态") return true, "already_logged_in" } c.LogInfo("未检测到登录状态,请登录账号...") // 等待用户手动登录,最多300秒 for i := 0; i < 300; i++ { if c.CheckLoginStatus() { c.SaveCookies() c.LogInfo("登录成功") return true, "login_success" } time.Sleep(1 * time.Second) } return false, "登录超时,请检查网络或账号状态" } // AskQuestion 提问并获取答案 func (c *DeepseekCollector) AskQuestion(question string) (string, error) { c.LogInfo(fmt.Sprintf("开始向DeepSeek提问: %s", question)) // 初始化浏览器 if err := c.SetupDriver(); err != nil { return "", fmt.Errorf("浏览器启动失败: %v", err) } defer c.Close() // 初始化页面 if err := c.InitPage(); err != nil { return "", fmt.Errorf("页面初始化失败,请先调用WaitLogin登录: %v", err) } c.Sleep(3) // 输入问题 if err := c.inputQuestion(question); err != nil { return "", fmt.Errorf("输入问题失败: %v", err) } // 点击发送 if err := c.clickSendButton(); err != nil { return "", fmt.Errorf("点击发送按钮失败: %v", err) } // 等待并获取答案 answer, err := c.waitForAnswer() if err != nil { return "", fmt.Errorf("获取答案失败: %v", err) } c.LogInfo(fmt.Sprintf("成功获取DeepSeek答案,长度: %d 字符", len(answer))) return answer, nil } // inputQuestion 输入问题 func (c *DeepseekCollector) inputQuestion(question string) error { c.LogInfo("输入问题到DeepSeek...") // DeepSeek的输入框选择器 inputSelectors := []string{ "textarea[placeholder*='输入']", "textarea[placeholder*='问']", "textarea", "[contenteditable='true']", ".chat-input textarea", "#message-input", } var inputBox *rod.Element var err error for _, selector := range inputSelectors { inputBox, err = c.WaitForElementVisible(selector, 10) if err == nil && inputBox != nil { c.LogInfo(fmt.Sprintf("找到输入框: %s", selector)) break } } if inputBox == nil { return fmt.Errorf("未找到输入框") } // 点击获取焦点 if err := inputBox.Click(proto.InputMouseButtonLeft, 1); err != nil { return fmt.Errorf("点击输入框失败: %v", err) } c.SleepMs(500) // 清空输入框 if err := c.ClearInput(inputBox); err != nil { c.LogInfo(fmt.Sprintf("清空输入框失败: %v", err)) } c.SleepMs(300) // 输入问题 if err := c.SetInputValue(inputBox, question); err != nil { inputBox.Input(question) } c.LogInfo(fmt.Sprintf("问题已输入")) c.SleepMs(1000) return nil } // clickSendButton 点击发送按钮 func (c *DeepseekCollector) clickSendButton() error { c.LogInfo("点击发送按钮...") // 发送按钮选择器 sendSelectors := []string{ "button[class*='send']", "button[class*='submit']", ".send-button", ".submit-button", "button svg[path*='send']", "[aria-label*='发送']", "[aria-label*='Send']", } var sendBtn *rod.Element var err error for _, selector := range sendSelectors { sendBtn, err = c.WaitForElementClickable(selector, 5) if err == nil && sendBtn != nil { c.LogInfo(fmt.Sprintf("找到发送按钮: %s", selector)) break } } if sendBtn == nil { // 尝试查找发送图标 sendBtn, err = c.Page.Element("button svg") if err != nil { return fmt.Errorf("未找到发送按钮") } } c.SleepMs(500) // 点击发送按钮 if err := c.JSClick(sendBtn); err != nil { return fmt.Errorf("点击发送按钮失败: %v", err) } c.LogInfo("已点击发送按钮") c.SleepMs(2000) return nil } // waitForAnswer 等待并获取答案 func (c *DeepseekCollector) waitForAnswer() (string, error) { c.LogInfo("等待DeepSeek回答...") timeout := 120 // 最大等待时间(秒) startTime := time.Now() lastAnswerLength := 0 for time.Since(startTime).Seconds() < float64(timeout) { // 查找答案区域 answerSelectors := []string{ ".message-content", ".response-content", "[class*='assistant'] [class*='content']", "[class*='ai'] [class*='message']", ".chat-message.ai", } for _, selector := range answerSelectors { answerElements, err := c.Page.Elements(selector) if err == nil && len(answerElements) > 0 { // 获取最后一个答案元素 lastAnswer := answerElements[len(answerElements)-1] visible, _ := lastAnswer.Visible() if visible { text, err := lastAnswer.Text() if err == nil && len(strings.TrimSpace(text)) > 0 { // 检查是否正在生成 isGenerating := strings.Contains(text, "正在") || strings.Contains(text, "思考") || strings.Contains(text, "generating") if !isGenerating { // 检查答案是否还在增长 currentLength := len(text) if currentLength == lastAnswerLength && currentLength > 10 { // 答案不再增长,认为已完成 c.LogInfo("获取到完整答案") return strings.TrimSpace(text), nil } lastAnswerLength = currentLength } } } } } c.SleepMs(1500) } return "", fmt.Errorf("等待答案超时") } // SafeElement 安全地获取元素 func (c *DeepseekCollector) SafeElement(selector string) (*rod.Element, error) { exists, _, err := c.Page.Has(selector) if err != nil { return nil, err } if !exists { return nil, nil } return c.Page.Element(selector) }