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