summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri Sokolyuk <demon@dim13.org>2015-07-19 22:42:15 +0200
committerDimitri Sokolyuk <demon@dim13.org>2015-07-19 22:42:15 +0200
commite9d3a039648f99a8b834bb600569b08bd94653bd (patch)
treecf13ccd4668a368d6a3b3eeff5455e5fa6f43fba
Initial import
-rw-r--r--duck.go107
1 files changed, 107 insertions, 0 deletions
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")
+}