/lib/ddg/search-client.js

https://github.com/VarioLabs/ddg-api.js · JavaScript · 155 lines · 70 code · 29 blank · 56 comment · 13 complexity · fb883a89c244d94278f1c9ad45178e9b MD5 · raw file

  1. /*!
  2. * DuckDuckGo Search Client for Node.js
  3. * Copyright(c) 2011 Sujal Shah <sujal@variolabs.com>
  4. * Source covered by the MIT License
  5. *
  6. * Portions from or inspired by the twitter-node library by
  7. * Jeff Waugh (https://github.com/jdub/node-twitter)
  8. * including the constructor/options storage
  9. *
  10. */
  11. var request = require('request')
  12. , url = require('url')
  13. , querystring = require('querystring')
  14. , _ = require('underscore');
  15. exports = module.exports;
  16. var SearchClient = function(options)
  17. {
  18. if (!(this instanceof SearchClient)) return new SearchClient(options);
  19. var defaults = {
  20. /*
  21. * output format (json or XML - defaults to json)
  22. * Required
  23. */
  24. format: "json"
  25. /*
  26. * pretty print json output
  27. */
  28. , prettyJson: false
  29. /*
  30. * use SSL
  31. * defaults to false
  32. */
  33. , useSSL: false
  34. /*
  35. * Set strict SSL mode
  36. */
  37. , strictSSL: false
  38. /*
  39. * Base URL for API, includes the question mark
  40. */
  41. , baseUrl: "http://api.duckduckgo.com/?"
  42. /*
  43. * SSL Base URL for API, includes the question mark
  44. */
  45. , sslBaseUrl: "https://api.duckduckgo.com/?"
  46. /*
  47. * Skip redirect for bang commands (defaults to true because
  48. * it doesn't make sense for an API client like this to redirect -
  49. * not really sure it should be an option)
  50. */
  51. , noRedirect: true
  52. /*
  53. * Remove HTML from text
  54. * defaults to false
  55. */
  56. , noHtml: false
  57. /*
  58. * Skip Disambiguation records in the response
  59. * Defaults to false
  60. */
  61. , skipDisambig: false
  62. /*
  63. * User Agent
  64. *
  65. */
  66. , userAgent: "DDG Search Client for Node.js ("+SearchClient.version+")"
  67. };
  68. if (options === null || typeof options !== 'object')
  69. options = {};
  70. // merge options passed in with defaults
  71. this.options = _.extend(defaults, options);
  72. }
  73. SearchClient.version = "0.0.1";
  74. module.exports = SearchClient;
  75. /*
  76. * search(query, options, callback)
  77. *
  78. * callback gets 3 arguments back, basically the responses from
  79. * the underlying request object, except that body is turned into objects
  80. */
  81. SearchClient.prototype.search = function(query, options, callback) {
  82. if (typeof options === 'function') {
  83. callback = options;
  84. options = null;
  85. }
  86. if ( typeof callback !== 'function' ) {
  87. throw "ERROR: Callback function required, was not present or of type function";
  88. return this;
  89. }
  90. var finalOptions = this.options;
  91. var arguments = {
  92. "q": query,
  93. "format": finalOptions.format,
  94. "no_html": (finalOptions.noHtml === true ? 1 : 0),
  95. "no_redirect": (finalOptions.noRedirect === true ? 1 : 0),
  96. "skip_disambig": (finalOptions.skipDisambig === true ? 1 : 0),
  97. "pretty": (finalOptions.prettyJson === true ? 1 : 0)
  98. };
  99. if (options !== null)
  100. {
  101. finalOptions = _.extend(this.options, options);
  102. }
  103. var finalBaseUrl = (finalOptions.useSSL) === true ? finalOptions.sslBaseUrl : finalOptions.baseUrl;
  104. request({
  105. uri: finalBaseUrl + querystring.stringify(arguments)
  106. , strictSSL: finalOptions.strictSSL
  107. , method: finalOptions.method || "GET"
  108. , headers: {
  109. "User-Agent": finalOptions.userAgent
  110. }
  111. , timeout: 2000
  112. }, function(error, response, body){
  113. if (!error && response.statusCode >= 200 && response.statusCode < 300)
  114. {
  115. // error could be in the body (DDG returns 200 for failed requests)
  116. if (body.length == 0)
  117. {
  118. error = new Error("DDG API Error: Empty response. Check your arguments for proper values: " + JSON.stringify(arguments));
  119. }
  120. callback(error, response, JSON.parse(body));
  121. } else {
  122. // probably should do something interesting here...
  123. callback(error, response, body);
  124. }
  125. });
  126. }