diff --git a/aard_inventory.xml b/aard_inventory.xml index b822b21..04a889d 100644 --- a/aard_inventory.xml +++ b/aard_inventory.xml @@ -72,6 +72,8 @@ dbot.wish : Module to track which wishes a character has purchased dbot.pagesize : Module to determine a character's current page size (# lines before page prompt) dbot.execute : Execute one or more commands without contention from user-entered commands dbot.callback : Module to help manage callback functions and parameters +dbot.remote : Module to retrieve remote files +dbot.version : Module to track version and changelog information and update the plugin --> @@ -85,7 +87,7 @@ dbot.callback : Module to help manage callback functions and parameters save_state="y" date_written="2017-08-12 08:45:15" requires="4.98" - version="2.0004" + version="2.0005" > <# of seconds> Plugin info - dinv version [check | update] + dinv version [check | changelog | update confirm] dinv help @@ -347,7 +349,7 @@ Feature Wishlist @w") + dbot.print("@W " .. pluginNameCmd .. " version @Y[check | changelog | update confirm]@w") end -- inv.cli.version.usage @@ -4429,17 +4436,22 @@ function inv.cli.version.examples() [[@W The version mode without arguments will tell you the version information for the plugin and format versions for components of the plugin. You can also check if -you have the latest official plugin release and update to that release if you are -not yet running the latest and greatest version of the plugin. +you have the latest official plugin release and optionally view the changelog +between your current version and the latest version. If you wish to upgrade to +the latest release, you can do that too :) Examples: 1) Display your current version information "@Gdinv version@W" 2) Compare your plugin version to the version of the latest published release + and display the changelog between your version and the latest release "@Gdinv version check@W" - 3) Check if you have the latest plugin version. If your version is not the + 3) Display the entire plugin changelog + "@Gdinv version changelog@W" + + 4) Check if you have the latest plugin version. If your version is not the latest and greatest, download the latest release and install it. You do not need to log out or restart mush. "@Gdinv version update confirm@W" @@ -15912,6 +15924,8 @@ dbot.wish : Module to track which wishes a character has purchased dbot.pagesize : Module to determine a character's current page size (# lines before page prompt) dbot.execute : Execute one or more commands without contention from user-entered commands dbot.callback : Module to help manage callback functions and parameters +dbot.remote : Module to retrieve remote files +dbot.version : Module to track version and changelog information and update the plugin --]] @@ -16081,146 +16095,6 @@ function dbot.reload() end -- dbot.reload ----------------------------------------------------------------------------------------------------- --- dbot.update: If the current plugin isn't the latest published version, update it, and reload it --- --- Note: This code is derived from a plugin written by Arcidayne. Thanks Arcidayne! ----------------------------------------------------------------------------------------------------- - -dbot.update = {} -dbot.update.url = "https://raw.githubusercontent.com/Aardurel/aard-plugins/master/aard_inventory.xml" -dbot.update.protocol = "HTTPS" -dbot.update.pkg = nil - -drlDbotUpdateCheck = "check" -drlDbotUpdateInstall = "install" - -function dbot.update.version(mode, endTag) - local retval = DRL_RET_SUCCESS - - if (mode == nil) or ((mode ~= drlDbotUpdateCheck) and (mode ~= drlDbotUpdateInstall)) then - dbot.warn("dbot.update.version: Missing or invalid mode parameter") - return inv.tags.stop(invTagsVersion, endTag, DRL_RET_INVALID_PARAM) - end -- if - - if (dbot.update.pkg ~= nil) then - dbot.info("Skipping update request: another update request is in progress") - return inv.tags.stop(invTagsVersion, endTag, DRL_RET_BUSY) - end -- if - - -- Pull in the async package if it exists - local asyncOk, async = pcall (require, "async") - if (not asyncOk) or (async == nil) then - dbot.warn("dbot.update.version: Failed to find \"async\" package, skipping update request") - return inv.tags.stop(invTagsVersion, endTag, DRL_RET_UNSUPPORTED) - end -- if - - dbot.update.pkg = {} - dbot.update.pkg.mode = mode - dbot.update.pkg.endTag = endTag - - -- Make a request to grab the latest plugin file. Newer versions of mush have nice async - -- capabilities and we use that if possible. Otherwise, we fall back to an old-style request. - -- Both methods of making the request will call dbot.update.callback upon completion. - if (async.doAsyncRemoveRequest ~= nil) then - async.doAsyncRemoteRequest(dbot.update.url, dbot.update.callback, dbot.update.protocol) - else - wait.make(dbot.updateCR) -- Fall back to a co-routine running the old-style async code - end - - return retval -end -- dbot.update - - --- Scan the file we just (hopefully) downloaded and check the file's version. If the user --- requested a version check, we report the current and latest available versions. If the --- user requested an installation of the latest plugin, do the upgrade if everything looks --- sane. -function dbot.update.callback(retval, page, status, headers, fullStatus, requestUrl) - local retval = DRL_RET_SUCCESS - - if (dbot.update.pkg == nil) or (dbot.update.pkg.mode == nil) then - dbot.error("dbot.update.callback: Missing or invalid update package detected") - return inv.tags.stop(invTagsVersion, "end tag is nil", DRL_RET_INVALID_PARAM) - end -- if - - local endTag = dbot.update.pkg.endTag - - if (status ~= 200) then - dbot.warn("dbot.update.callback: Failed to retrieve remote plugin information") - retval = DRL_RET_INTERNAL_ERROR - - else - local currentVersion = GetPluginInfo(GetPluginID(), 19) or 0 - local currentVerStr = string.format("%1.4f", currentVersion) - local remoteVerStr = string.match(page, '%s%s+version="([0-9%.]+)"') - local remoteVersion = tonumber(remoteVerStr or "") or 0 - - if (remoteVersion == currentVersion) then - dbot.info("You are running the most recent plugin (v" .. currentVerStr .. ")") - - elseif (remoteVersion < currentVersion) then - dbot.warn("Your current plugin (v" .. currentVerStr .. ") " .. - "is newer than the latest official release (v" .. remoteVerStr .. ")") - retval = DRL_RET_VER_MISMATCH - - elseif (dbot.update.pkg.mode == drlDbotUpdateCheck) then - dbot.info("You are running v" .. currentVerStr .. ", latest version is v" .. remoteVerStr) - - elseif (dbot.update.pkg.mode == drlDbotUpdateInstall) then - dbot.info("Updating plugin from version " .. currentVerStr .. " to version " .. remoteVerStr) - dbot.info("Please do not enter anything until the update completes") - - local pluginFile = GetPluginInfo(GetPluginID(), 6) - local file = io.open(pluginFile, "w") - file:write(page) - file:close() - dbot.reload() - - else - dbot.error("dbot.update.callback: Detected invalid mode \"@R" .. (dbot.update.pkg.mode or "nil") .. - "@W\"") - end -- if - - end -- if - - dbot.update.pkg = nil - return inv.tags.stop(invTagsVersion, endTag, retval) - -end -- dbot.update.callback - - -function dbot.updateCR() - local urlThread = async.request(dbot.update.url, dbot.update.protocol) - local updateRet, page, status, headers, fullStatus = -1, nil, -1, nil, nil, dbot.update.url - - if (urlThread == nil) then - dbot.warn("dbot.updateCR: Failed to create thread requesting update") - - else - local timeout = 10 - local totTime = 0 - while (urlThread:alive()) do - if (totTime > timeout) then - retval = DRL_RET_TIMEOUT - break - end -- if - - wait.time(drlSpinnerPeriodDefault) - totTime = totTime + drlSpinnerPeriodDefault - end -- while - - if (retval ~= DRL_RET_SUCCESS) then - updateRet, page, status, headers, fullStatus = urlThread:join() - end -- if - - end -- if - - dbot.update.callback(updateRet, page, status, headers, fullStatus) - -end -- dbot.updateCR - - ---------------------------------------------------------------------------------------------------- -- dbot.shell: Run a shell command in the background without pulling up a command prompt window ---------------------------------------------------------------------------------------------------- @@ -19576,6 +19450,376 @@ function dbot.callback.wait(resultData, timeout, period) end -- dbot.callback.wait +---------------------------------------------------------------------------------------------------- +-- Module to retrieve remote files +-- +-- dbot.remote.get(url, protocol) +-- dbot.remote.getCR() +-- +---------------------------------------------------------------------------------------------------- + +dbot.remote = {} +dbot.remote.getPkg = nil + +-- Blocks and then returns file, retval +-- Must be called from within a co-routine +function dbot.remote.get(url, protocol) + local retval = DRL_RET_SUCCESS + local fileData = nil + + if (url == nil) or (url == "") then + dbot.warn("dbot.remote.get: missing url parameter") + return fileData, DRL_RET_INVALID_PARAMETER + end -- if + + if (protocol == nil) or (protocol == "") then + dbot.warn("dbot.remote.get: missing protocol parameter") + return fileData, DRL_RET_INVALID_PARAMETER + end -- if + + if (dbot.remote.getPkg ~= nil) then + dbot.info("Skipping remote request: another request is in progress") + return fileData, DRL_RET_BUSY + end -- if + + dbot.remote.getPkg = {} + dbot.remote.getPkg.url = url + dbot.remote.getPkg.protocol = protocol + dbot.remote.getPkg.isDone = false + + wait.make(dbot.remote.getCR) + + local timeout = 10 + local totTime = 0 + while (dbot.remote.getPkg.isDone == false) do + if (totTime > timeout) then + retval = DRL_RET_TIMEOUT + break + end -- if + + wait.time(drlSpinnerPeriodDefault) + totTime = totTime + drlSpinnerPeriodDefault + end -- while + + if (dbot.remote.getPkg ~= nil) and (dbot.remote.getPkg.fileData ~= nil) then + fileData = dbot.remote.getPkg.fileData + else + dbot.warn("dbot.remote.get: Failed to find data for file \"@G" .. url .. "@W\"") + retval = DRL_RET_MISSING_ENTRY + end -- if + + dbot.remote.getPkg = nil + return fileData, retval + +end -- dbot.remote.get + + +function dbot.remote.getCR() + local retval = DRL_RET_SUCCESS + + if (dbot.remote.getPkg == nil) or (dbot.remote.getPkg.url == nil) then + dbot.error("dbot.remote.getCR: remote package is nil or corrupted!") + dbot.remote.getPkg = nil + return DRL_RET_INTERNAL_ERROR + end -- if + + local urlThread = async.request(dbot.remote.getPkg.url, dbot.remote.getPkg.protocol) + + if (urlThread == nil) then + dbot.warn("dbot.remote.getCR: Failed to create thread requesting remote data") + retval = DRL_RET_INTERNAL_ERROR + + else + local timeout = 10 + local totTime = 0 + while (urlThread:alive()) do + if (totTime > timeout) then + retval = DRL_RET_TIMEOUT + break + end -- if + + wait.time(drlSpinnerPeriodDefault) + totTime = totTime + drlSpinnerPeriodDefault + end -- while + + local remoteRet, page, status, headers, fullStatus = urlThread:join() + + if (status ~= 200) then + dbot.warn("dbot.remote.getCR: Failed to retrieve remote file") + retval = DRL_RET_INTERNAL_ERROR + else + dbot.remote.getPkg.fileData = page + end -- if + + dbot.remote.getPkg.isDone = true + + end -- if + + return retval +end -- dbot.remote.getCR + + +---------------------------------------------------------------------------------------------------- +-- dbot.version: Track the plugin's version and changelog and update the plugin +-- +-- dbot.version.changelog(minVersion, endTag) +-- dbot.version.changelogCR() +-- dbot.version.displayChanges(minVersion, changeLog) +-- dbot.version.displayChange(changeLogEntries) +-- +-- +-- Note: This code is derived from a plugin written by Arcidayne. Thanks Arcidayne! +---------------------------------------------------------------------------------------------------- + +dbot.version = {} +dbot.version.changeLogPkg = nil + +drlDbotChangeLogTypeFix = "@RFix@W" +drlDbotChangeLogTypeNew = "@GNew@W" +drlDbotChangeLogTypeMisc = "@yMsc@W" + + +function dbot.version.changelog(minVersion, endTag) + local url = "https://raw.githubusercontent.com/Aardurel/aard-plugins/master/aard_inventory.changelog" + local protocol = "HTTPS" + + if (dbot.version.changeLogPkg ~= nil) then + dbot.info("Skipping changelog request: another request is in progress") + return inv.tags.stop(invTagsVersion, endTag, DRL_RET_BUSY) + end -- if + + dbot.version.changeLogPkg = {} + dbot.version.changeLogPkg.url = url + dbot.version.changeLogPkg.protocol = protocol + dbot.version.changeLogPkg.minVersion = minVersion or 0 + dbot.version.changeLogPkg.endTag = endTag + + wait.make(dbot.version.changelogCR) + + return DRL_RET_SUCCESS +end -- dbot.version.changelog + + +function dbot.version.changelogCR() + + if (dbot.version.changeLogPkg == nil) then + dbot.error("dbot.version.changelogCR: Change log package is missing!") + return inv.tags.stop(invTagsVersion, "missing end tag", DRL_RET_INTERNAL_ERROR) + end -- if + + local fileData, retval = dbot.remote.get(dbot.version.changeLogPkg.url, dbot.version.changeLogPkg.protocol) + if (retval ~= DRL_RET_SUCCESS) then + dbot.warn("dbot.version.changelogCR: Failed to retrieve remote changelog file: " .. + dbot.retval.getString(retval)) + elseif (fileData == nil) then + dbot.info("No changelog information was found.") + + else + loadstring(fileData)() + if (dbot.changelog == nil) then + dbot.warn("dbot.version.changelogCR: Invalid changelog format detected") + retval = DRL_RET_INTERNAL_ERROR + else + retval = dbot.version.displayChanges(dbot.version.changeLogPkg.minVersion, dbot.changelog) + end -- if + end -- if + + dbot.version.changeLogPkg = nil + + return inv.tags.stop(invTagsVersion, endTag, retval) + +end -- dbot.version.changelogCR + + +function dbot.version.displayChanges(minVersion, changeLog) + local sortedLog = {} + + for k, v in pairs(changeLog) do + table.insert(sortedLog, { version = tonumber(k) or 0, changes = v}) + end -- for + + table.sort(sortedLog, function (v1, v2) return v1.version > v2.version end) + + for _, clog in ipairs(sortedLog) do + if (clog.version > minVersion) then + dbot.version.displayChange(clog) + end -- if + end -- for + + return DRL_RET_SUCCESS +end -- dbot.version.displayChanges + + +-- Format of entry is: { version = 2.0004, +-- changes = { { change = drlDbotChangeLogTypeXYZ, desc = "what changed" } +-- } +-- } +function dbot.version.displayChange(changeLogEntries) + local retval = DRL_RET_SUCCESS + + if (changeLogEntries == nil) then + dbot.warn("dbot.version.displayChange: Change entries are missing!") + return DRL_RET_INVALID_PARAM + end -- if + + dbot.print(string.format("@Cv%1.4f@W", changeLogEntries.version)) + for _, logEntry in ipairs(changeLogEntries.changes) do + dbot.print(string.format("@W (%s): %s", logEntry.change, logEntry.desc)) + end -- for + + return retval +end -- dbot.version.displayChange + + + + + + + + + +--FIXME: rename this version.update??? +dbot.update = {} +dbot.update.url = "https://raw.githubusercontent.com/Aardurel/aard-plugins/master/aard_inventory.xml" +dbot.update.protocol = "HTTPS" +dbot.update.pkg = nil + +drlDbotUpdateCheck = "check" +drlDbotUpdateInstall = "install" + +function dbot.update.version(mode, endTag) + local retval = DRL_RET_SUCCESS + + if (mode == nil) or ((mode ~= drlDbotUpdateCheck) and (mode ~= drlDbotUpdateInstall)) then + dbot.warn("dbot.update.version: Missing or invalid mode parameter") + return inv.tags.stop(invTagsVersion, endTag, DRL_RET_INVALID_PARAM) + end -- if + + if (dbot.update.pkg ~= nil) then + dbot.info("Skipping update request: another update request is in progress") + return inv.tags.stop(invTagsVersion, endTag, DRL_RET_BUSY) + end -- if + + -- Pull in the async package if it exists + local asyncOk, async = pcall (require, "async") + if (not asyncOk) or (async == nil) then + dbot.warn("dbot.update.version: Failed to find \"async\" package, skipping update request") + return inv.tags.stop(invTagsVersion, endTag, DRL_RET_UNSUPPORTED) + end -- if + + dbot.update.pkg = {} + dbot.update.pkg.mode = mode + dbot.update.pkg.endTag = endTag + + -- Make a request to grab the latest plugin file. Newer versions of mush have nice async + -- capabilities and we use that if possible. Otherwise, we fall back to an old-style request. + -- Both methods of making the request will call dbot.update.callback upon completion. + if (async.doAsyncRemoveRequest ~= nil) then + async.doAsyncRemoteRequest(dbot.update.url, dbot.update.callback, dbot.update.protocol) + else + wait.make(dbot.updateCR) -- Fall back to a co-routine running the old-style async code + end + + return retval +end -- dbot.update + + +-- Scan the file we just (hopefully) downloaded and check the file's version. If the user +-- requested a version check, we report the current and latest available versions. If the +-- user requested an installation of the latest plugin, do the upgrade if everything looks +-- sane. +function dbot.update.callback(retval, page, status, headers, fullStatus, requestUrl) + local retval = DRL_RET_SUCCESS + + if (dbot.update.pkg == nil) or (dbot.update.pkg.mode == nil) then + dbot.error("dbot.update.callback: Missing or invalid update package detected") + return inv.tags.stop(invTagsVersion, "end tag is nil", DRL_RET_INVALID_PARAM) + end -- if + + local endTag = dbot.update.pkg.endTag + + if (status ~= 200) then + dbot.warn("dbot.update.callback: Failed to retrieve remote plugin information") + retval = DRL_RET_INTERNAL_ERROR + + else + local currentVersion = GetPluginInfo(GetPluginID(), 19) or 0 + local currentVerStr = string.format("%1.4f", currentVersion) + local remoteVerStr = string.match(page, '%s%s+version="([0-9%.]+)"') + local remoteVersion = tonumber(remoteVerStr or "") or 0 + + if (remoteVersion == currentVersion) then + dbot.info("You are running the most recent plugin (v" .. currentVerStr .. ")") + + elseif (remoteVersion < currentVersion) then + dbot.warn("Your current plugin (v" .. currentVerStr .. ") " .. + "is newer than the latest official release (v" .. remoteVerStr .. ")") + retval = DRL_RET_VER_MISMATCH + + elseif (dbot.update.pkg.mode == drlDbotUpdateCheck) then + dbot.info("You are running v" .. currentVerStr .. ", latest version is v" .. remoteVerStr) + dbot.info("Changes since your last update:") + dbot.update.pkg = nil + return dbot.version.changelog(currentVersion, endTag) + + elseif (dbot.update.pkg.mode == drlDbotUpdateInstall) then + dbot.info("Updating plugin from version " .. currentVerStr .. " to version " .. remoteVerStr) + dbot.info("Please do not enter anything until the update completes") + + local pluginFile = GetPluginInfo(GetPluginID(), 6) + local file = io.open(pluginFile, "w") + file:write(page) + file:close() + dbot.reload() + + else + dbot.error("dbot.update.callback: Detected invalid mode \"@R" .. (dbot.update.pkg.mode or "nil") .. + "@W\"") + end -- if + + end -- if + + dbot.update.pkg = nil + return inv.tags.stop(invTagsVersion, endTag, retval) + +end -- dbot.update.callback + + +function dbot.updateCR() + local urlThread = async.request(dbot.update.url, dbot.update.protocol) + local updateRet, page, status, headers, fullStatus = -1, nil, -1, nil, nil, dbot.update.url + + if (urlThread == nil) then + dbot.warn("dbot.updateCR: Failed to create thread requesting update") + + else + local timeout = 10 + local totTime = 0 + while (urlThread:alive()) do + if (totTime > timeout) then + retval = DRL_RET_TIMEOUT + break + end -- if + + wait.time(drlSpinnerPeriodDefault) + totTime = totTime + drlSpinnerPeriodDefault + end -- while + +--FIXME: this is broken!!! retval is never defined!!! + if (retval ~= DRL_RET_SUCCESS) then + updateRet, page, status, headers, fullStatus = urlThread:join() + end -- if + + end -- if + + dbot.update.callback(updateRet, page, status, headers, fullStatus) + +end -- dbot.updateCR + + + + + ]]>