PageRenderTime 186ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/sopel/modules/search.py

https://gitlab.com/daniellawrence/sopel
Python | 133 lines | 119 code | 4 blank | 10 comment | 4 complexity | fe8baa41ae73ac2de2c070cf0cf89680 MD5 | raw file
  1. # coding=utf-8
  2. """
  3. search.py - Sopel Web Search Module
  4. Copyright 2008-9, Sean B. Palmer, inamidst.com
  5. Copyright 2012, Edward Powell, embolalia.net
  6. Licensed under the Eiffel Forum License 2.
  7. http://sopel.chat
  8. """
  9. from __future__ import unicode_literals, absolute_import, print_function, division
  10. import re
  11. from sopel import web
  12. from sopel.module import commands, example
  13. import json
  14. import sys
  15. if sys.version_info.major < 3:
  16. from urllib import quote_plus
  17. else:
  18. from urllib.parse import quote_plus
  19. def formatnumber(n):
  20. """Format a number with beautiful commas."""
  21. parts = list(str(n))
  22. for i in range((len(parts) - 3), 0, -3):
  23. parts.insert(i, ',')
  24. return ''.join(parts)
  25. r_bing = re.compile(r'<h3><a href="([^"]+)"')
  26. def bing_search(query, lang='en-GB'):
  27. base = 'http://www.bing.com/search?mkt=%s&q=' % lang
  28. bytes = web.get(base + query)
  29. m = r_bing.search(bytes)
  30. if m:
  31. return m.group(1)
  32. r_duck = re.compile(r'nofollow" class="[^"]+" href="(.*?)">')
  33. def duck_search(query):
  34. query = query.replace('!', '')
  35. uri = 'http://duckduckgo.com/html/?q=%s&kl=uk-en' % query
  36. bytes = web.get(uri)
  37. if 'web-result"' in bytes: # filter out the adds on top of the page
  38. bytes = bytes.split('web-result"')[1]
  39. m = r_duck.search(bytes)
  40. if m:
  41. return web.decode(m.group(1))
  42. # Alias google_search to duck_search
  43. google_search = duck_search
  44. def duck_api(query):
  45. if '!bang' in query.lower():
  46. return 'https://duckduckgo.com/bang.html'
  47. # This fixes issue #885 (https://github.com/sopel-irc/sopel/issues/885)
  48. # It seems that duckduckgo api redirects to its Instant answer API html page
  49. # if the query constains special charactares that aren't urlencoded.
  50. # So in order to always get a JSON response back the query is urlencoded
  51. query = quote_plus(query)
  52. uri = 'http://api.duckduckgo.com/?q=%s&format=json&no_html=1&no_redirect=1' % query
  53. results = json.loads(web.get(uri))
  54. if results['Redirect']:
  55. return results['Redirect']
  56. else:
  57. return None
  58. @commands('duck', 'ddg', 'g')
  59. @example('.duck privacy or .duck !mcwiki obsidian')
  60. def duck(bot, trigger):
  61. """Queries Duck Duck Go for the specified input."""
  62. query = trigger.group(2)
  63. if not query:
  64. return bot.reply('.ddg what?')
  65. # If the API gives us something, say it and stop
  66. result = duck_api(query)
  67. if result:
  68. bot.reply(result)
  69. return
  70. # Otherwise, look it up on the HTMl version
  71. uri = duck_search(query)
  72. if uri:
  73. bot.reply(uri)
  74. if 'last_seen_url' in bot.memory:
  75. bot.memory['last_seen_url'][trigger.sender] = uri
  76. else:
  77. bot.reply("No results found for '%s'." % query)
  78. @commands('search')
  79. @example('.search nerdfighter')
  80. def search(bot, trigger):
  81. """Searches Bing and Duck Duck Go."""
  82. if not trigger.group(2):
  83. return bot.reply('.search for what?')
  84. query = trigger.group(2)
  85. bu = bing_search(query) or '-'
  86. du = duck_search(query) or '-'
  87. if bu == du:
  88. result = '%s (b, d)' % bu
  89. else:
  90. if len(bu) > 150:
  91. bu = '(extremely long link)'
  92. if len(du) > 150:
  93. du = '(extremely long link)'
  94. result = '%s (b), %s (d)' % (bu, du)
  95. bot.reply(result)
  96. @commands('suggest')
  97. def suggest(bot, trigger):
  98. """Suggest terms starting with given input"""
  99. if not trigger.group(2):
  100. return bot.reply("No query term.")
  101. query = trigger.group(2)
  102. uri = 'http://websitedev.de/temp-bin/suggest.pl?q='
  103. answer = web.get(uri + query.replace('+', '%2B'))
  104. if answer:
  105. bot.say(answer)
  106. else:
  107. bot.reply('Sorry, no result.')