From e9d3a039648f99a8b834bb600569b08bd94653bd Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Sun, 19 Jul 2015 22:42:15 +0200 Subject: Initial import --- duck.go | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 duck.go diff --git a/duck.go b/duck.go new file mode 100644 index 0000000..131b890 --- /dev/null +++ b/duck.go @@ -0,0 +1,107 @@ +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 + ImageWidth int + ImageHeight int + ImageIsLogo int + 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 +) + +func do(q string, flags queryFlags) ([]byte, error) { + v := url.Values{} + v.Set("q", url.QueryEscape(q)) + v.Set("format", "json") + if q[0] == '!' { + v.Set("no_redirect", "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") +} -- cgit v1.2.3