/goduckgo/duckduck.go
Go | 183 lines | 116 code | 25 blank | 42 comment | 16 complexity | f169b61af4c875462e118ff84801e0ed MD5 | raw file
- // Package goduckgo provides the functionality for using
- // DuckDuckGo API. For the description of the API, visit
- // http://duckduckgo.com/api.html.
- package goduckgo
- import (
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "net/url"
- "os"
- "strings"
- )
- var baseUrl = "https://api.duckduckgo.com/?q=%s&format=json&pretty=1%s"
- // Message is a structure containing all the information returned by
- // DDG for a query.
- // Abstract: topic summary (can contain HTML, e.g. italics)
- // AbstractText: topic summary (with no HTML)
- // AbstractSource: name of Abstract source
- // AbstractURL: deep link to expanded topic page in AbstractSource
- // Image: link to image that goes with Abstract
- // Heading: name of topic that goes with Abstract
- // Answer: instant answer
- // 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).
- // Definition: dictionary definition (may differ from Abstract)
- // DefinitionSource: name of Definition source
- // DefinitionURL: deep link to expanded definition page in DefinitionSource
- // RelatedTopics: array of internal links to related topics associated with Abstract
- // Results: array of external links associated with Abstract
- // Type: response category, i.e. A (article), D (disambiguation), C (category), N (name), E (exclusive), or nothing.
- // Redirect: !bang redirect URL
- type Message struct {
- Definition string
- DefinitionSource string
- Heading string
- AbstractText string
- Abstract string
- AbstractSource string
- Image string
- Type string
- AnswerType string
- Redirect string
- DefinitionURL string
- Answer string
- AbstractURL string
- Results Results
- RelatedTopics RelatedTopics
- }
- // Decode a message given a HTTP response body
- func (message *Message) Decode(body []byte) error {
- if err := json.Unmarshal(body, message); err != nil {
- return err
- }
- return nil
- }
- // Show Result as standard output
- func (result *Result) Show(prefix string) {
- result.fshow(os.Stdout, prefix)
- }
- func (result *Result) fshow(w io.Writer, prefix string) {
- fmt.Fprintln(w, prefix, "Result:", result.Result)
- if !result.Icon.IsEmpty() {
- fmt.Fprintln(w, prefix, "Icon:")
- result.Icon.fshow(w, prefix+prefix)
- }
- fmt.Fprintln(w, prefix, "First URL:", result.FirstURL)
- fmt.Fprintln(w, prefix, "Text:", result.Text)
- }
- type Results []Result
- type RelatedTopics []RelatedTopic
- // Result is an external link associated with Abstract
- // Result: HTML link(s) to external site(s)
- // FirstURL: first URL in Result
- // Icon: icon associated with FirstURL
- // Text: text from FirstURL
- type Result RelatedTopic
- // RelatedTopic is a internal link to related topics associated with Abstract
- // Result: HTML link to a related topic
- // FirstURL: first URL in Result
- // Icon: icon associated with related topic
- // Text: text from first URL
- type RelatedTopic struct {
- Result string
- Icon Icon
- FirstURL string
- Text string
- }
- // Show RelatedTopic as standard output
- func (topic *RelatedTopic) Show(prefix string) {
- topic.fshow(os.Stdout, prefix)
- }
- func (topic *RelatedTopic) fshow(w io.Writer, prefix string) {
- fmt.Fprintln(w, prefix, "Result:", topic.Result)
- if !topic.Icon.IsEmpty() {
- fmt.Fprintln(w, prefix, "Icon:")
- topic.Icon.fshow(w, prefix+prefix)
- }
- fmt.Fprintln(w, prefix, "First URL:", topic.FirstURL)
- fmt.Fprintln(w, prefix, "Text:", topic.Text)
- }
- // Icon associated with related topics
- // URL: URL of icon
- // Height: height of icon (px)
- // Width: width of icon (px)
- type Icon struct {
- URL string
- Height interface{} // can be string or number ("16" or 16)
- Width interface{} // can be string or number ("16" or 16)
- }
- // IsEmpty if all Icon fields are empty
- func (icon *Icon) IsEmpty() bool {
- return icon.URL == "" &&
- icon.Height == "" &&
- icon.Width == ""
- }
- // Show Show as standard output
- func (icon *Icon) Show(prefix string) {
- icon.fshow(os.Stdout, prefix)
- }
- func (icon *Icon) fshow(w io.Writer, prefix string) {
- fmt.Fprintln(w, prefix, "URL:", icon.URL)
- fmt.Fprintln(w, prefix, "Height:", icon.Height)
- fmt.Fprintln(w, prefix, "Width:", icon.Width)
- }
- // EncodeUrl given a text query
- func EncodeUrl(query string) string {
- queryEnc := url.QueryEscape(query)
- if strings.HasPrefix(query, "!") {
- return fmt.Sprintf(baseUrl, queryEnc, "&no_redirect=1")
- }
- return fmt.Sprintf(baseUrl, queryEnc, "")
- }
- // Do the HTTP requests against API and handle errors
- func Do(url string) ([]byte, error) {
- resp, err := http.Get(url)
- if err != nil {
- return nil, err
- }
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, err
- }
- return body, nil
- }
- // Query the API given a text query, returns a Message
- func Query(query string) (*Message, error) {
- ddgUrl := EncodeUrl(query)
- body, err := Do(ddgUrl)
- if err != nil {
- return nil, err
- }
- message := &Message{}
- if err = message.Decode(body); err != nil {
- return nil, err
- }
- return message, nil
- }