hackernews.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. # SPDX-License-Identifier: AGPL-3.0-or-later
  2. """Hackernews
  3. """
  4. from datetime import datetime
  5. from urllib.parse import urlencode
  6. from dateutil.relativedelta import relativedelta
  7. from flask_babel import gettext
  8. # Engine metadata
  9. about = {
  10. "website": "https://news.ycombinator.com/",
  11. "wikidata_id": "Q686797",
  12. "official_api_documentation": "https://hn.algolia.com/api",
  13. "use_official_api": True,
  14. "require_api_key": False,
  15. "results": "JSON",
  16. }
  17. # Engine configuration
  18. paging = True
  19. time_range_support = True
  20. categories = ["it"]
  21. results_per_page = 30
  22. # Search URL
  23. base_url = "https://hn.algolia.com/api/v1"
  24. def request(query, params):
  25. search_type = 'search'
  26. if not query:
  27. # if search query is empty show results from HN's front page
  28. search_type = 'search_by_date'
  29. query_params = {
  30. "tags": "front_page",
  31. "page": (params["pageno"] - 1),
  32. }
  33. else:
  34. query_params = {
  35. "query": query,
  36. "page": (params["pageno"] - 1),
  37. "hitsPerPage": results_per_page,
  38. "minWordSizefor1Typo": 4,
  39. "minWordSizefor2Typos": 8,
  40. "advancedSyntax": "true",
  41. "ignorePlurals": "false",
  42. "minProximity": 7,
  43. "numericFilters": '[]',
  44. "tagFilters": '["story",[]]',
  45. "typoTolerance": "true",
  46. "queryType": "prefixLast",
  47. "restrictSearchableAttributes": '["title","comment_text","url","story_text","author"]',
  48. "getRankingInfo": "true",
  49. }
  50. if params['time_range']:
  51. search_type = 'search_by_date'
  52. timestamp = (datetime.now() - relativedelta(**{f"{params['time_range']}s": 1})).timestamp()
  53. query_params["numericFilters"] = f"created_at_i>{timestamp}"
  54. params["url"] = f"{base_url}/{search_type}?{urlencode(query_params)}"
  55. return params
  56. def response(resp):
  57. results = []
  58. data = resp.json()
  59. for hit in data["hits"]:
  60. object_id = hit["objectID"]
  61. points = hit.get("points") or 0
  62. num_comments = hit.get("num_comments") or 0
  63. metadata = ""
  64. if points != 0 or num_comments != 0:
  65. metadata = f"{gettext('points')}: {points}" f" | {gettext('comments')}: {num_comments}"
  66. results.append(
  67. {
  68. "title": hit.get("title") or f"{gettext('author')}: {hit['author']}",
  69. "url": f"https://news.ycombinator.com/item?id={object_id}",
  70. "content": hit.get("url") or hit.get("comment_text") or hit.get("story_text") or "",
  71. "metadata": metadata,
  72. "author": hit["author"],
  73. "publishedDate": datetime.utcfromtimestamp(hit["created_at_i"]),
  74. }
  75. )
  76. return results