Introduced DuckDuckGo as a new search provider alongside Google. Implemented a flexible caching system with in-memory, file-based, and no-op cache options to improve modularity. Updated dependencies and revised the project structure for improved maintainability.
84 lines
1.5 KiB
Go
84 lines
1.5 KiB
Go
package search
|
|
|
|
import (
|
|
"answer/pkg/cache"
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"gitea.stevedudenhoeffer.com/steve/go-extractor"
|
|
|
|
"gitea.stevedudenhoeffer.com/steve/go-extractor/sites/duckduckgo"
|
|
)
|
|
|
|
type duckDuckGo struct {
|
|
Cache cache.Cache
|
|
Browser extractor.Browser
|
|
}
|
|
|
|
func NewDuckDuckGo(c cache.Cache) (Search, error) {
|
|
timeout := 60 * time.Second
|
|
browser, err := extractor.NewPlayWrightBrowser(extractor.PlayWrightBrowserOptions{Timeout: &timeout})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create browser: %w", err)
|
|
}
|
|
|
|
return duckDuckGo{
|
|
Cache: c,
|
|
Browser: browser,
|
|
}, nil
|
|
}
|
|
|
|
var _ Search = duckDuckGo{}
|
|
|
|
func (d duckDuckGo) Search(ctx context.Context, search string) ([]Result, error) {
|
|
var res []Result
|
|
|
|
key := "duckduckgo:" + search
|
|
|
|
err := d.Cache.GetJSON(key, &res)
|
|
|
|
if err == nil {
|
|
return res, nil
|
|
}
|
|
|
|
results, err := d.searchDuckDuckGo(ctx, search)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, r := range results {
|
|
res = append(res, Result{
|
|
Title: r.Title,
|
|
URL: r.URL,
|
|
Description: r.Description,
|
|
})
|
|
}
|
|
|
|
_ = d.Cache.SetJSON(key, res)
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (d duckDuckGo) searchDuckDuckGo(ctx context.Context, search string) ([]Result, error) {
|
|
cfg := duckduckgo.DefaultConfig
|
|
|
|
r, err := cfg.Search(ctx, d.Browser, search)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res := make([]Result, len(r))
|
|
|
|
for i, v := range r {
|
|
res[i] = Result{
|
|
URL: v.URL,
|
|
Title: v.Title,
|
|
Description: v.Description,
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|