package main
import (
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"bytes"
"github.com/PuerkitoBio/goquery"
)
func die(err error) {
if (err != nil) {
fmt.Printf("%=+v\n", err)
os.Exit(1)
}
}
func quit(message string, exitCode int) {
fmt.Println(message)
os.Exit(exitCode)
}
func getNodeText(sel *goquery.Selection, cssSel string) string {
return strings.Trim(sel.Find(cssSel).Eq(0).Text(), " \t\n\r")
}
const userAgent = "websearch/0.1 (textmode; Linux x86_64; 1024x768)"
const ddgUrl = "https://html.duckduckgo.com/html"
// See https://duckduckgo.com/params for more.
type DDGQuery struct {
q string // search terms
b string // ?
df string // duration, but not sure of what
kf string // favicons, -1 is off
kh string // https, 1 is on
kl string // region of search
kp string // Safe search, 1 is on
k1 string // advertisements, -1 is off
}
func makeQuery(qs []string) string {
qsEscaped := []string{}
for _, str := range qs {
qsEscaped = append(qsEscaped, url.QueryEscape(str))
}
query := DDGQuery{
q: strings.Join(qsEscaped, "+"),
b: "",
df: "",
kf: "-1",
kl: "us-en",
kh: "1",
kp: "1",
k1: "-1",
}
queryStr := fmt.Sprintf("q=%s", query.q)
queryStr += fmt.Sprintf("&b=%s", url.QueryEscape(query.b))
queryStr += fmt.Sprintf("&df=%s", url.QueryEscape(query.df))
queryStr += fmt.Sprintf("&kf=%s", url.QueryEscape(query.kf))
queryStr += fmt.Sprintf("&kh=%s", url.QueryEscape(query.kh))
queryStr += fmt.Sprintf("&kl=%s", url.QueryEscape(query.kl))
queryStr += fmt.Sprintf("&kp=%s", url.QueryEscape(query.kp))
queryStr += fmt.Sprintf("&k1=%s", url.QueryEscape(query.k1))
return queryStr
}
func main() {
flag.Parse()
searchWords := flag.Args()
if len(searchWords) <= 0 {
quit("Enter a search", 1)
}
reqBody := makeQuery(searchWords)
req, err := http.NewRequest("GET", ddgUrl + "?" + reqBody , nil); die(err)
req.Header.Set("User-Agent", userAgent)
req.Header.Set("DNT", "1")
res, err := http.DefaultClient.Do(req); die(err); defer res.Body.Close()
resBytes, err := io.ReadAll(res.Body); die(err)
goqueryDoc, err := goquery.NewDocumentFromReader(bytes.NewReader(resBytes)); die(err)
results := goqueryDoc.Find(".web-result")
if len(results.Nodes) == 0 {
quit(fmt.Sprintf("No results for %s\n", reqBody), 0)
}
report := ""
for i := len(results.Nodes) - 1; i >= 0; i-- {
r := results.Eq(i)
report += fmt.Sprintf(
"R%02d: %s\n%s\n%s\n\n",
i+1,
getNodeText(r, ".result__title"),
getNodeText(r, ".result__url"),
getNodeText(r, ".result__snippet"),
)
}
fmt.Print(report)
}