hackernews.py 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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 = (
  53. # pylint: disable=unexpected-keyword-arg
  54. datetime.now()
  55. - relativedelta(**{f"{params['time_range']}s": 1}) # type: ignore
  56. ).timestamp()
  57. query_params["numericFilters"] = f"created_at_i>{timestamp}"
  58. params["url"] = f"{base_url}/{search_type}?{urlencode(query_params)}"
  59. return params
  60. def response(resp):
  61. results = []
  62. data = resp.json()
  63. for hit in data["hits"]:
  64. object_id = hit["objectID"]
  65. points = hit.get("points") or 0
  66. num_comments = hit.get("num_comments") or 0
  67. metadata = ""
  68. if points != 0 or num_comments != 0:
  69. metadata = f"{gettext('points')}: {points}" f" | {gettext('comments')}: {num_comments}"
  70. results.append(
  71. {
  72. "title": hit.get("title") or f"{gettext('author')}: {hit['author']}",
  73. "url": f"https://news.ycombinator.com/item?id={object_id}",
  74. "content": hit.get("url") or hit.get("comment_text") or hit.get("story_text") or "",
  75. "metadata": metadata,
  76. "author": hit["author"],
  77. "publishedDate": datetime.utcfromtimestamp(hit["created_at_i"]),
  78. }
  79. )
  80. return results