PageRenderTime 52ms CodeModel.GetById 32ms app.highlight 10ms RepoModel.GetById 0ms app.codeStats 0ms

/goduckgo/duckduck.go

http://github.com/ajanicij/goduckgo
Go | 183 lines | 116 code | 25 blank | 42 comment | 16 complexity | f169b61af4c875462e118ff84801e0ed MD5 | raw file
  1// Package goduckgo provides the functionality for using
  2// DuckDuckGo API. For the description of the API, visit
  3// http://duckduckgo.com/api.html.
  4package goduckgo
  5
  6import (
  7	"encoding/json"
  8	"fmt"
  9	"io"
 10	"io/ioutil"
 11	"net/http"
 12	"net/url"
 13	"os"
 14	"strings"
 15)
 16
 17var baseUrl = "https://api.duckduckgo.com/?q=%s&format=json&pretty=1%s"
 18
 19// Message is a structure containing all the information returned by
 20// DDG for a query.
 21//   Abstract: topic summary (can contain HTML, e.g. italics)
 22//   AbstractText: topic summary (with no HTML)
 23//   AbstractSource: name of Abstract source
 24//   AbstractURL: deep link to expanded topic page in AbstractSource
 25//   Image: link to image that goes with Abstract
 26//   Heading: name of topic that goes with Abstract
 27//   Answer: instant answer
 28//   AnswerType: type of Answer, e.g. calc, color, digest, info, ip, iploc, phone, pw, rand, regexp, unicode, upc, or zip (see goodies & tech pages for examples).
 29//   Definition: dictionary definition (may differ from Abstract)
 30//   DefinitionSource: name of Definition source
 31//   DefinitionURL: deep link to expanded definition page in DefinitionSource
 32//   RelatedTopics: array of internal links to related topics associated with Abstract
 33//   Results: array of external links associated with Abstract
 34//   Type: response category, i.e. A (article), D (disambiguation), C (category), N (name), E (exclusive), or nothing.
 35//   Redirect: !bang redirect URL
 36type Message struct {
 37	Definition       string
 38	DefinitionSource string
 39	Heading          string
 40	AbstractText     string
 41	Abstract         string
 42	AbstractSource   string
 43	Image            string
 44	Type             string
 45	AnswerType       string
 46	Redirect         string
 47	DefinitionURL    string
 48	Answer           string
 49	AbstractURL      string
 50	Results          Results
 51	RelatedTopics    RelatedTopics
 52}
 53
 54// Decode a message given a HTTP response body
 55func (message *Message) Decode(body []byte) error {
 56	if err := json.Unmarshal(body, message); err != nil {
 57		return err
 58	}
 59
 60	return nil
 61}
 62
 63// Show Result as standard output
 64func (result *Result) Show(prefix string) {
 65	result.fshow(os.Stdout, prefix)
 66}
 67
 68func (result *Result) fshow(w io.Writer, prefix string) {
 69	fmt.Fprintln(w, prefix, "Result:", result.Result)
 70	if !result.Icon.IsEmpty() {
 71		fmt.Fprintln(w, prefix, "Icon:")
 72		result.Icon.fshow(w, prefix+prefix)
 73	}
 74	fmt.Fprintln(w, prefix, "First URL:", result.FirstURL)
 75	fmt.Fprintln(w, prefix, "Text:", result.Text)
 76}
 77
 78type Results []Result
 79
 80type RelatedTopics []RelatedTopic
 81
 82// Result is an external link associated with Abstract
 83//   Result: HTML link(s) to external site(s)
 84//   FirstURL: first URL in Result
 85//   Icon: icon associated with FirstURL
 86//   Text: text from FirstURL
 87type Result RelatedTopic
 88
 89// RelatedTopic is a internal link to related topics associated with Abstract
 90//	 Result: HTML link to a related topic
 91//   FirstURL: first URL in Result
 92//   Icon: icon associated with related topic
 93// 	 Text: text from first URL
 94type RelatedTopic struct {
 95	Result   string
 96	Icon     Icon
 97	FirstURL string
 98	Text     string
 99}
100
101// Show RelatedTopic as standard output
102func (topic *RelatedTopic) Show(prefix string) {
103	topic.fshow(os.Stdout, prefix)
104}
105
106func (topic *RelatedTopic) fshow(w io.Writer, prefix string) {
107	fmt.Fprintln(w, prefix, "Result:", topic.Result)
108	if !topic.Icon.IsEmpty() {
109		fmt.Fprintln(w, prefix, "Icon:")
110		topic.Icon.fshow(w, prefix+prefix)
111	}
112	fmt.Fprintln(w, prefix, "First URL:", topic.FirstURL)
113	fmt.Fprintln(w, prefix, "Text:", topic.Text)
114}
115
116// Icon associated with related topics
117//   URL: URL of icon
118//   Height: height of icon (px)
119//   Width: width of icon (px)
120type Icon struct {
121	URL    string
122	Height interface{} // can be string or number ("16" or 16)
123	Width  interface{} // can be string or number ("16" or 16)
124}
125
126// IsEmpty if all Icon fields are empty
127func (icon *Icon) IsEmpty() bool {
128	return icon.URL == "" &&
129		icon.Height == "" &&
130		icon.Width == ""
131}
132
133// Show Show as standard output
134func (icon *Icon) Show(prefix string) {
135	icon.fshow(os.Stdout, prefix)
136}
137
138func (icon *Icon) fshow(w io.Writer, prefix string) {
139	fmt.Fprintln(w, prefix, "URL:", icon.URL)
140	fmt.Fprintln(w, prefix, "Height:", icon.Height)
141	fmt.Fprintln(w, prefix, "Width:", icon.Width)
142}
143
144// EncodeUrl given a text query
145func EncodeUrl(query string) string {
146	queryEnc := url.QueryEscape(query)
147	if strings.HasPrefix(query, "!") {
148		return fmt.Sprintf(baseUrl, queryEnc, "&no_redirect=1")
149	}
150	return fmt.Sprintf(baseUrl, queryEnc, "")
151}
152
153// Do the HTTP requests against API and handle errors
154func Do(url string) ([]byte, error) {
155	resp, err := http.Get(url)
156	if err != nil {
157		return nil, err
158	}
159
160	body, err := ioutil.ReadAll(resp.Body)
161	if err != nil {
162		return nil, err
163	}
164
165	return body, nil
166}
167
168// Query the API given a text query, returns a Message
169func Query(query string) (*Message, error) {
170	ddgUrl := EncodeUrl(query)
171
172	body, err := Do(ddgUrl)
173	if err != nil {
174		return nil, err
175	}
176
177	message := &Message{}
178	if err = message.Decode(body); err != nil {
179		return nil, err
180	}
181
182	return message, nil
183}