package duck import ( "encoding/json" "errors" "io/ioutil" "net/http" "net/url" ) const baseURL = "https://api.duckduckgo.com/" type Answer struct { Abstract string // topic summary (can contain HTML, e.g. italics) AbstractText string // topic summary (with no HTML) AbstractSource string // name of Abstract source AbstractURL string // deep link to expanded topic page in AbstractSource Image string // link to image that goes with Abstract Heading string // name of topic that goes with Abstract Answer string // instant answer AnswerType string // type of Answer, e.g. calc, color, digest, info, ip, iploc, phone, pw, rand, regexp, unicode, upc, or zip Definition string // dictionary definition (may differ from Abstract) DefinitionSource string // name of Definition source DefinitionURL string // deep link to expanded definition page in DefinitionSource RelatedTopics []Result // array of internal links to related topics associated with Abstract Results []Result // array of external links associated with Abstract Type string // response category, i.e. A (article), D (disambiguation), C (category), N (name), E (exclusive), or nothing Redirect string // !bang redirect URL Entity string } type Result struct { Result string // HTML link(s) to related topic(s)/external site(s) FirstURL string // first URL in Result Icon Icon `json:"omitempty"` // icon associated with related topic(s)/FirstURL Text string // text from FirstURL } type Icon struct { URL string // URL of icon Height int // height of icon (px) Width int // width of icon (px) } type queryFlags int const ( noHTML queryFlags = 1 << iota skipDisambig prettyJSON ) func do(q string, flags queryFlags) ([]byte, error) { v := url.Values{} v.Set("q", q) v.Set("format", "json") if q[0] == '!' { v.Set("no_redirect", "1") } if flags&prettyJSON != 0 { v.Set("pretty", "1") } if flags&noHTML != 0 { v.Set("no_html", "1") } if flags&skipDisambig != 0 { v.Set("skip_disambig", "1") } resp, err := http.PostForm(baseURL, v) if err != nil { return nil, err } defer resp.Body.Close() return ioutil.ReadAll(resp.Body) } func decode(b []byte) (Answer, error) { a := Answer{} return a, json.Unmarshal(b, &a) } func Query(q string) (Answer, error) { body, err := do(q, 0) if err != nil { return Answer{}, err } return decode(body) } func Abstract(q string) (string, error) { body, err := do(q, noHTML|skipDisambig) if err != nil { return "", err } a, err := decode(body) if err != nil { return "", err } if a.AbstractText != "" { return a.AbstractText, nil } if len(a.RelatedTopics) > 0 { if t := a.RelatedTopics[0].Text; t != "" { return t, nil } } return "", errors.New("empty result") }