82 lines
2.2 KiB
Go
82 lines
2.2 KiB
Go
|
|
package placement
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"fmt"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// DiscoverChannel identifies which backend fulfilled a placement discover query.
|
|||
|
|
type DiscoverChannel string
|
|||
|
|
|
|||
|
|
const (
|
|||
|
|
DiscoverThreadsAPI DiscoverChannel = "threads_api"
|
|||
|
|
DiscoverBrave DiscoverChannel = "brave"
|
|||
|
|
DiscoverCrawler DiscoverChannel = "crawler"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// DiscoverRequest is used by scan jobs; expand-graph only uses Brave knowledge_expand.
|
|||
|
|
type DiscoverRequest struct {
|
|||
|
|
Query string
|
|||
|
|
Keyword string // plain tag for crawler; optional
|
|||
|
|
Recency bool
|
|||
|
|
Limit int
|
|||
|
|
Member MemberContext
|
|||
|
|
Crawler CrawlerSearchFn
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type DiscoverPost struct {
|
|||
|
|
Text string
|
|||
|
|
Permalink string
|
|||
|
|
ExternalID string
|
|||
|
|
Author string
|
|||
|
|
PostedAt string
|
|||
|
|
LikeCount int
|
|||
|
|
ReplyCount int
|
|||
|
|
Source DiscoverChannel
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Discover runs keyword discovery respecting the member's connection prefs.
|
|||
|
|
// Formal mode (dev_mode=false) never falls back to crawler.
|
|||
|
|
func Discover(ctx context.Context, req DiscoverRequest) ([]DiscoverPost, DiscoverChannel, error) {
|
|||
|
|
m := req.Member
|
|||
|
|
if m.DevMode {
|
|||
|
|
if !m.BrowserConnected {
|
|||
|
|
return nil, "", fmt.Errorf("開發模式需先同步 Chrome Session")
|
|||
|
|
}
|
|||
|
|
if req.Crawler == nil {
|
|||
|
|
return nil, DiscoverCrawler, fmt.Errorf("crawler search not configured")
|
|||
|
|
}
|
|||
|
|
keyword := CrawlerKeywordFromQuery(req.Query, req.Keyword)
|
|||
|
|
if keyword == "" {
|
|||
|
|
return nil, DiscoverCrawler, fmt.Errorf("crawler keyword is empty")
|
|||
|
|
}
|
|||
|
|
posts, err := req.Crawler(ctx, m, keyword, req.Limit)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, DiscoverCrawler, err
|
|||
|
|
}
|
|||
|
|
return posts, DiscoverCrawler, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if m.AllowsThreadsAPI {
|
|||
|
|
if !m.ApiConnected {
|
|||
|
|
return nil, "", fmt.Errorf("正式模式需先完成 Threads API 連線")
|
|||
|
|
}
|
|||
|
|
posts, err := keywordSearchViaThreadsAPI(ctx, req)
|
|||
|
|
if err == nil && len(posts) > 0 {
|
|||
|
|
return posts, DiscoverThreadsAPI, nil
|
|||
|
|
}
|
|||
|
|
if err != nil && !m.AllowsBrave {
|
|||
|
|
return nil, "", err
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if m.AllowsBrave {
|
|||
|
|
if m.BraveAPIKey == "" {
|
|||
|
|
return nil, "", fmt.Errorf("請在設定頁設定 Brave Search API key(跟隨此登入帳號)")
|
|||
|
|
}
|
|||
|
|
return nil, DiscoverBrave, fmt.Errorf("brave threads discover delegated to worker")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return nil, "", fmt.Errorf("目前搜尋來源模式無可用管道:%s", m.SearchSourceMode)
|
|||
|
|
}
|