update_detector.lua 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. -- Luanti
  2. -- Copyright (C) 2023 rubenwardy
  3. -- SPDX-License-Identifier: LGPL-2.1-or-later
  4. update_detector = {}
  5. if not core.get_http_api then
  6. update_detector.get_all = function() return {} end
  7. update_detector.get_count = function() return 0 end
  8. return
  9. end
  10. assert(core.create_dir(core.get_cache_path() .. DIR_DELIM .. "cdb"))
  11. local cache_file_path = core.get_cache_path() .. DIR_DELIM .. "cdb" .. DIR_DELIM .. "updates.json"
  12. local has_fetched = false
  13. local latest_releases
  14. do
  15. if check_cache_age("cdb_updates_last_checked", 24 * 3600) then
  16. local f = io.open(cache_file_path, "r")
  17. local data = ""
  18. if f then
  19. data = f:read("*a")
  20. f:close()
  21. end
  22. data = data ~= "" and core.parse_json(data) or nil
  23. if type(data) == "table" then
  24. latest_releases = data
  25. has_fetched = true
  26. end
  27. end
  28. end
  29. local function fetch_latest_releases()
  30. local version = core.get_version()
  31. local base_url = core.settings:get("contentdb_url")
  32. local url = base_url ..
  33. "/api/updates/?type=mod&type=game&type=txp&protocol_version=" ..
  34. core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
  35. local http = core.get_http_api()
  36. local response = http.fetch_sync({ url = url })
  37. if not response.succeeded then
  38. return
  39. end
  40. return core.parse_json(response.data)
  41. end
  42. --- Get a table from package key (author/name) to latest release id
  43. ---
  44. --- @param callback function that takes a single argument, table or nil
  45. local function get_latest_releases(callback)
  46. core.handle_async(fetch_latest_releases, nil, callback)
  47. end
  48. local function has_packages_from_cdb()
  49. for _, content in pairs(pkgmgr.get_all()) do
  50. if pkgmgr.get_contentdb_id(content) then
  51. return true
  52. end
  53. end
  54. return false
  55. end
  56. --- @returns a new table with all keys lowercase
  57. local function lowercase_keys(tab)
  58. local ret = {}
  59. for key, value in pairs(tab) do
  60. ret[key:lower()] = value
  61. end
  62. return ret
  63. end
  64. local function fetch()
  65. if has_fetched or not has_packages_from_cdb() then
  66. return
  67. end
  68. has_fetched = true
  69. get_latest_releases(function(releases)
  70. if not releases then
  71. has_fetched = false
  72. return
  73. end
  74. latest_releases = lowercase_keys(releases)
  75. core.safe_file_write(cache_file_path, core.write_json(latest_releases))
  76. cache_settings:set("cdb_updates_last_checked", tostring(os.time()))
  77. if update_detector.get_count() > 0 then
  78. local maintab = ui.find_by_name("maintab")
  79. if not maintab.hidden then
  80. ui.update()
  81. end
  82. end
  83. end)
  84. end
  85. --- @returns a list of content with an update available
  86. function update_detector.get_all()
  87. if latest_releases == nil then
  88. fetch()
  89. return {}
  90. end
  91. local ret = {}
  92. local all_content = pkgmgr.get_all()
  93. for _, content in ipairs(all_content) do
  94. local cdb_id = pkgmgr.get_contentdb_id(content)
  95. if cdb_id then
  96. -- The backend will account for aliases in `latest_releases`
  97. local latest_release = latest_releases[cdb_id]
  98. if not latest_release and content.type == "game" then
  99. latest_release = latest_releases[cdb_id .. "_game"]
  100. end
  101. if latest_release and latest_release > content.release then
  102. ret[#ret + 1] = content
  103. end
  104. end
  105. end
  106. return ret
  107. end
  108. --- @return number of packages with updates available
  109. function update_detector.get_count()
  110. return #update_detector.get_all()
  111. end