Module:Exchange

From the RuneScape Wiki, the wiki for all things RuneScape
Jump to navigation Jump to search
Module documentation
This documentation is transcluded from Module:Exchange/doc. [edit] [history] [purge]
Module:Exchange is invoked by .
Module:Exchange requires Module:Addcommas.
Module:Exchange requires Module:Yesno.
Module:Exchange loads data from Module:Exchange/< ... >.
Module:Exchange is required by .
Function list
L 13 — p.checkTitle
L 28 — load
L 56 — p.loadBulkData
L 91 — p._price
L 119 — p._price_simple
L 133 — p._limit
L 143 — p._value
L 153 — p._volume
L 167 — p._itemId
L 177 — p._alchable
L 193 — p._alchmultiplier
L 208 — alchval
L 225 — p._highalch
L 235 — p._lowalch
L 249 — p._diff
L 267 — p.exists
L 282 — p._exists
L 301 — p.price
L 353 — p.gemwlimit
L 373 — p.view

Powers most exchange pages and exchange-related modules and templates. If you only need to load a few prices in a module, consider using Module:ExchangeLite instead; for large amount of prices use this module instead as it offers much better performance at the cost of a larger overhead.

This module is a helper module to be used by other modules; it may not be designed to be invoked directly. See RuneScape:Lua/Helper modules for a full list and more information. For a full list of modules using this helper click here

FunctionTypeUse
_price( item, [multi|1], [format|false], [round|nil], [default|nil])string, number, boolean, boolean, numberGets the current GE price of item, multiplied by multi (default 1), rounded to round decimal places (default unrounded), and if format is true, formatted with thousands separators (default false). If a default number value is given, that value will be used instead if the item cannot be found. If format is true a string is returned, otherwise a number is returned.
_priceViaModule( item, [multi|1], [format|true], [round|nil] )string, number, boolean, booleanSame as _price() but slower and a smaller overhead. Can be used if you only need a few prices but consider using Module:ExchangeLite instead.
_value( item )stringReturns the value of item as a number.
_highalch( item )stringReturns the high alch value of item as a number, accounting for alch multipliers.
_lowalch( item )stringReturns the low alch value of item as a number, accounting for alch multipliers.
_alchable( item )stringReturns the alchablility of item as a boolean.
_alchmultiplier( item )stringReturns the alchemy multiplier of item (default 0.6) as a number.
_limit( item )stringReturns the GE limit of item as a number.
_diff( item, [format|false] )string, booleanReturns the difference of the current GE price and the previous GE price for item as a number. If format is true, return a string of the difference formatted with thousands separators.
_exists( item )stringChecks if the module for item exists, returns a boolean.
Example:
local exchange = require( 'module:Exchange' )
mw.log( exchange._price( 'Rune dagger' ) ) --> 9684
mw.log( exchange._price( 'Rune dagger', 1.055, true ) ) --> '10,216.62'
mw.log( exchange._price( 'Rune dagger', 1.055, true, 0 ) ) --> '10,217'
mw.log( exchange._price( 'Rune dagger', 1.055, true, -3 ) ) --> '10,000'
mw.log( exchange._priceViaModule( 'Rune dagger' ) ) --> 9684
mw.log( exchange._value( 'Rune dagger' ) ) --> 1668
mw.log( exchange._highalch( 'Rune dagger' ) ) --> 1000
mw.log( exchange._lowalch( 'Rune dagger' ) ) --> 667
mw.log( exchange._alchable( 'Rune dagger' ) ) --> true
mw.log( exchange._alchmultiplier( 'Rune dagger' ) ) --> 0.6
mw.log( exchange._limit( 'Rune dagger' ) ) --> 100
mw.log( exchange._diff( 'Rune dagger' ) ) --> 0
mw.log( exchange._exists( 'Rune dagger' ) ) --> true

-- <nowiki>
--
-- Implements various exchange templates
-- See Individual method docs for more details
--
local p = {}
local yesno = require('Module:Yesno')
local addcommas = require('Module:Addcommas')._add

--
-- Makes sure first letter of item is uppercase
--
function p.checkTitle( item )
	item = item:gsub( '&#0?39;', "'" )
	item = item:gsub( '_', ' ' )
	item = item:gsub( '  +', ' ' )
	item = item:gsub( '^%l', string.upper )
	return item
end

--
-- Simple mw.loadData wrapper used to access data located on module subpages
--
-- @param item {string} Item to retrieve data for
-- @param suppress_err {boolean} (optional) If true and item data can not be loaded, return nil instead of error()
-- @return {table} Table of item data
--
local function load( item, suppress_err )
	item = p.checkTitle( item )
	local noErr, ret = pcall( mw.loadData, 'Module:Exchange/' .. item )

	if noErr then
		return ret
	elseif suppress_err then
		return nil
	end

	error( ret )
end

local data_module_names = {
	price = 'Module:GEPrices/data.json',
	volume = 'Module:GEVolumes/data.json',
	lastPrice = 'Module:LastPrices/data.json',
	limit = 'Module:GELimits/data.json'
}

local data_historical_keys = {
	price = 'price',
	lastPrice = 'last',
	limit = 'limit'
}

local loaded_data_modules = {}

function p.loadBulkData( key, data_type, suppress_err )
	local module_name = data_module_names[data_type]
	if loaded_data_modules[module_name] == nil then
		loaded_data_modules[module_name] = mw.loadJsonData(module_name)
	end
	if key ~= '%LAST_UPDATE_F%' and key ~= '%LAST_UPDATE%' then
		key = p.checkTitle( key )
	end
	if loaded_data_modules[module_name][key] then
		return loaded_data_modules[module_name][key]
	end
	if not data_historical_keys[data_type] then
		return nil
	end
	local exchange_data = load(key, true)
	if exchange_data and exchange_data.historical then
		return exchange_data[data_historical_keys[data_type]]
	end
	if suppress_err then
		return nil
	end
	error('price not found for ' .. key)
end

--
-- Returns the price of an item
--
-- @param item {string} Item to get current price of
-- @param multi {number} (optional) Multiplies the output price by the specified number
-- @param format {boolean} (optional) Format the result with commas (defaults to false)
-- @param round {number} (optional) Round the result to a number of decimal places
-- @param default Any non-nil value to return as the price if item's data can not be found.
-- @param tax {boolean} (optional) Subtract exchange tax from the output price.
-- @return {number|string} Price of item. Will return a string if formatted, else a number.
--
function p._price( item, multi, format, round, default, tax )
	local price = p.loadBulkData( item, 'price', default ~= nil )
	local multi = type( multi ) == 'number' and multi or 1
	local format = type( format ) == 'boolean' and format or false
	local ret

	if price then
		if tax and item ~= 'Bond' then
			price = price - math.floor(price * 0.02)
		end
		ret = price * multi

		-- round the number to X d.p.
		if round ~= nil then
			local multi = 10^( round )
			ret = math.floor( ret * multi + 0.5 ) / multi
		end

		if format then
			return addcommas( ret )
		end

		return ret
	else
		return default
	end
end

function p._price_simple( key )
	local module_name = data_module_names['price']
	if loaded_data_modules[module_name] == nil then
		loaded_data_modules[module_name] = mw.loadJsonData(module_name)
	end
	return loaded_data_modules[module_name][key]
end

--
-- Returns the limit of an item
--
-- @param item {string} Item to get the limit of
-- @return {number} Limit of item
--
function p._limit( item )
	return load( item ).limit
end

--
-- Returns the value of an item
--
-- @param item {string} Item to get the value for
-- @return {number} Value of item
--
function p._value( item )
	return load( item ).value
end

--
-- Returns the volume of an item
--
-- @param item {string} Item to get the limit of
-- @return {number} Volume of item
--
function p._volume( item, default )
	local vol = p.loadBulkData(item, 'volume', default ~= nil)
	if not vol then
		return default
	end
	return vol
end

--
-- Returns the itemId of an item
--
-- @param item {string} Item to get the itemId for
-- @return {number} itemId of item
--
function p._itemId( item )
	return load( item ).itemId
end

--
-- Returns the alchability of an item
--
-- @param item {string} Item to get the alchability of
-- @return {boolean} Alchability
--
function p._alchable( item )
	local a = load( item ).alchable
	if a == nil or a == true then
		return true
	elseif a == false then
		return false
	end
	return nil
end

--
-- Returns the alch multiplier of an item
--
-- @param item {string} Item to get the multiplier or
-- @return {number} Multiplier
--
function p._alchmultiplier( item )
	local a = load( item ).alchmultiplier
	if type(a) == 'number' then
		return a
	end
	return 0.6
end

--
-- Internal function for alch values
--
-- @param item {string} Item to get the high alch for
-- @param mul {number} Alchemy multiplier
-- @return {number} Alch value of item
--
local function alchval(item, mul)
	if p._alchable(item) then
		local v = p._value(item)
		local m = p._alchmultiplier(item)
		if v then
			return math.max(math.floor(v * m * mul), 1)
		end
	end
	return -1
end

--
-- Returns the high alch value of an item
--
-- @param item {string} Item to get the high alch for
-- @return {number} High alch of item
--
function p._highalch( item )
	return alchval(item, 1)
end

--
-- Returns the low alch value of an item
--
-- @param item {string} Item to get the low alch for
-- @return {number} Low alch of item
--
function p._lowalch( item )
	return alchval(item, 2/3)
end

--
-- Calculates the difference between the current price and the last price of an item
--
-- @param item {string} Item to calculate price difference for
-- @param format {boolean} `true` if the output is to be formatted with commas
--                         Defaults to `false`
-- @return {number|string} The price difference as a number
--                         If `format` is set to `true` then this returns a string
--                         If either of the prices to calculate the diff from are unavailable, this returns `0` (number)
--
function p._diff( item, format )
	local diff = 0
	local price = p.loadBulkData(item, 'price')
	local lastPrice = p.loadBulkData(item, 'lastPrice')
	if price and lastPrice then
		diff = price - lastPrice

		if format then
			diff = addcommas( diff )
		end
	end

	return diff
end

--
-- {{GEExists}}
--
function p.exists( frame )
	local args = frame:getParent().args
	local item = p.checkTitle( args[1] or '' )
	local noErr, data = pcall( mw.loadData, 'Module:Exchange/' .. item )

	if noErr then
		return '1'
	end

	return '0'
end

--
-- GEExists for modules
--
function p._exists( arg )
	local item = p.checkTitle( arg or '' )
	local noErr, data = pcall( mw.loadData, 'Module:Exchange/' .. item )

	if noErr then
		return true
	end

	return false
end

--
-- {{GEP}}
-- {{GEPrice}}
--
-- @example {{GEPrice|<item>|<format>|<multi>}}
-- @example {{GEPrice|<item>|<multi>}}
-- @example {{GEP|<item>|<multi>}}
--
function p.price( frame )
	-- usage: {{foo|item|format|multi}} or {{foo|item|multi}}
	local args = frame.args
	local pargs = frame:getParent().args
	local item = pargs[1]
	local expr = mw.ext.ParserFunctions.expr
	local round = tonumber( pargs.round )
	local tax = false

	if item then
		item = mw.text.trim( item )
	else
		error( '"item" argument not specified', 0 )
	end

	-- default to formatted for backwards compatibility with old GE templates
	local format = true
	local multi = 1

	-- format is set with #invoke
	-- so set it first to allow it to be overridden by template args
	if args.format ~= nil then
		format = yesno( args.format )
	end
	if args.tax ~= nil then
		tax = yesno( args.tax )
	end

	if tonumber( pargs[2] ) ~= nil then
		multi = tonumber( pargs[2] )

	-- indicated someone is trying to pass an equation as a mulitplier
	-- known use cases are fractions, but pass it to #expr to make sure it's handled correctly
	elseif pargs[2] ~= nil and string.find( pargs[2], '[/*+-]' ) then
		multi = tonumber( expr( pargs[2] ) )

	-- uses elseif to prevent something like {{GEP|Foo|1}}
	-- causing a formatted output, as 1 casts to true when passed to yesno
	elseif type( yesno( pargs[2] ) ) == 'boolean' then
		format = yesno( pargs[2] )

		if tonumber( pargs[3] ) ~= nil then
			multi = tonumber( pargs[3] )
		end
	end 

	return p._price( item, multi, format, round, pargs.dflt, tax )
end

--
-- experimental limit method for [[Grand Exchange/Buying Limits]]
--
function p.gemwlimit( frame )
	local item  = frame:getParent().args[1]
	local data = mw.loadData( 'Module:Exchange/' .. item )

	return data.limit
end

--
-- {{ExchangeItem}}
-- {{GEDiff}}
-- {{GELimit}}
-- {{GEValue}}
-- {{GEId}}
--
-- @example {{ExchangeItem|<item>}}
-- @example {{GEDiff|<item>}}
-- @example {{GELimit|<item>}}
-- @example {{GEValue|<item>}}
-- @example {{GEId|<item>}}
--
function p.view( frame )
	local fargs = frame.args
	local pargs = frame:getParent().args
	local item = pargs[1] or fargs.item
	local view = fargs.view or ''
	local loadView = {limit=true, value=true, itemId=true, members=true, category=true, examine=true, alchable=true}

	if item then
		item = mw.text.trim( item )
	else
		error( '"item" argument not specified', 0 )
	end

	view = string.lower( view )

	if view == 'itemid' then
		view = 'itemId'
	end

	if view == 'diff' then
		return p._diff( item )

	elseif view == 'hialch' or view == 'highalch' or view == 'high_alch' then
		return p._highalch(item)

	elseif view == 'lowalch' or view == 'low_alch' then
		return p._lowalch(item)

	elseif loadView[view] then
		return load( item )[view]
	end
end

return p
-- </nowiki>