mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 11:05:54 +01:00 
			
		
		
		
	refactor: async/await rewards
This commit is contained in:
		| @@ -1,143 +1,80 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
| var plugins = require('../plugins'); | ||||
| var db = require('../database'); | ||||
| const plugins = require('../plugins'); | ||||
| const db = require('../database'); | ||||
| const utils = require('../utils'); | ||||
|  | ||||
| var rewards = module.exports; | ||||
| const rewards = module.exports; | ||||
|  | ||||
| rewards.save = function (data, callback) { | ||||
| 	async.each(data, function save(data, next) { | ||||
| rewards.save = async function (data) { | ||||
| 	async function save(data) { | ||||
| 		if (!Object.keys(data.rewards).length) { | ||||
| 			return next(); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		var rewardsData = data.rewards; | ||||
| 		const rewardsData = data.rewards; | ||||
| 		delete data.rewards; | ||||
|  | ||||
| 		async.waterfall([ | ||||
| 			function (next) { | ||||
| 				if (!parseInt(data.id, 10)) { | ||||
| 					db.incrObjectField('global', 'rewards:id', next); | ||||
| 				} else { | ||||
| 					next(null, data.id); | ||||
| 				} | ||||
| 			}, | ||||
| 			function (rid, next) { | ||||
| 				data.id = rid; | ||||
|  | ||||
| 				async.series([ | ||||
| 					function (next) { | ||||
| 						rewards.delete(data, next); | ||||
| 					}, | ||||
| 					function (next) { | ||||
| 						db.setAdd('rewards:list', data.id, next); | ||||
| 					}, | ||||
| 					function (next) { | ||||
| 						db.setObject('rewards:id:' + data.id, data, next); | ||||
| 					}, | ||||
| 					function (next) { | ||||
| 						db.setObject('rewards:id:' + data.id + ':rewards', rewardsData, next); | ||||
| 					}, | ||||
| 				], next); | ||||
| 			}, | ||||
| 		], next); | ||||
| 	}, function (err) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		if (!parseInt(data.id, 10)) { | ||||
| 			data.id = await db.incrObjectField('global', 'rewards:id'); | ||||
| 		} | ||||
|  | ||||
| 		saveConditions(data, callback); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| rewards.delete = function (data, callback) { | ||||
| 	async.parallel([ | ||||
| 		function (next) { | ||||
| 			db.setRemove('rewards:list', data.id, next); | ||||
| 		}, | ||||
| 		function (next) { | ||||
| 			db.delete('rewards:id:' + data.id, next); | ||||
| 		}, | ||||
| 		function (next) { | ||||
| 			db.delete('rewards:id:' + data.id + ':rewards', next); | ||||
| 		}, | ||||
| 	], callback); | ||||
| }; | ||||
|  | ||||
| rewards.get = function (callback) { | ||||
| 	async.parallel({ | ||||
| 		active: getActiveRewards, | ||||
| 		conditions: function (next) { | ||||
| 			plugins.fireHook('filter:rewards.conditions', [], next); | ||||
| 		}, | ||||
| 		conditionals: function (next) { | ||||
| 			plugins.fireHook('filter:rewards.conditionals', [], next); | ||||
| 		}, | ||||
| 		rewards: function (next) { | ||||
| 			plugins.fireHook('filter:rewards.rewards', [], next); | ||||
| 		}, | ||||
| 	}, callback); | ||||
| }; | ||||
|  | ||||
| function saveConditions(data, callback) { | ||||
| 	var rewardsPerCondition = {}; | ||||
| 	async.waterfall([ | ||||
| 		function (next) { | ||||
| 			db.delete('conditions:active', next); | ||||
| 		}, | ||||
| 		function (next) { | ||||
| 			var conditions = []; | ||||
|  | ||||
| 			data.forEach(function (reward) { | ||||
| 				conditions.push(reward.condition); | ||||
| 				rewardsPerCondition[reward.condition] = rewardsPerCondition[reward.condition] || []; | ||||
| 				rewardsPerCondition[reward.condition].push(reward.id); | ||||
| 			}); | ||||
|  | ||||
| 			db.setAdd('conditions:active', conditions, next); | ||||
| 		}, | ||||
| 		function (next) { | ||||
| 			async.each(Object.keys(rewardsPerCondition), function (condition, next) { | ||||
| 				db.setAdd('condition:' + condition + ':rewards', rewardsPerCondition[condition], next); | ||||
| 			}, next); | ||||
| 		}, | ||||
| 	], function (err) { | ||||
| 		callback(err); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function getActiveRewards(callback) { | ||||
| 	var activeRewards = []; | ||||
|  | ||||
| 	function load(id, next) { | ||||
| 		async.parallel({ | ||||
| 			main: function (next) { | ||||
| 				db.getObject('rewards:id:' + id, next); | ||||
| 			}, | ||||
| 			rewards: function (next) { | ||||
| 				db.getObject('rewards:id:' + id + ':rewards', next); | ||||
| 			}, | ||||
| 		}, function (err, data) { | ||||
| 			if (data.main) { | ||||
| 				data.main.disabled = data.main.disabled === 'true'; | ||||
| 				data.main.rewards = data.rewards; | ||||
| 				activeRewards.push(data.main); | ||||
| 			} | ||||
|  | ||||
| 			next(err); | ||||
| 		}); | ||||
| 		await rewards.delete(data); | ||||
| 		await db.setAdd('rewards:list', data.id); | ||||
| 		await db.setObject('rewards:id:' + data.id, data); | ||||
| 		await db.setObject('rewards:id:' + data.id + ':rewards', rewardsData); | ||||
| 	} | ||||
|  | ||||
| 	db.getSetMembers('rewards:list', function (err, rewards) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
| 	await Promise.all(data.map(data => save(data))); | ||||
| 	await saveConditions(data); | ||||
| }; | ||||
|  | ||||
| 		async.eachSeries(rewards, load, function (err) { | ||||
| 			callback(err, activeRewards); | ||||
| 		}); | ||||
| rewards.delete = async function (data) { | ||||
| 	await Promise.all([ | ||||
| 		db.setRemove('rewards:list', data.id), | ||||
| 		db.delete('rewards:id:' + data.id), | ||||
| 		db.delete('rewards:id:' + data.id + ':rewards'), | ||||
| 	]); | ||||
| }; | ||||
|  | ||||
| rewards.get = async function () { | ||||
| 	return await utils.promiseParallel({ | ||||
| 		active: getActiveRewards, | ||||
| 		conditions: plugins.fireHook('filter:rewards.conditions', []), | ||||
| 		conditionals: plugins.fireHook('filter:rewards.conditionals', []), | ||||
| 		rewards: plugins.fireHook('filter:rewards.rewards', []), | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| async function saveConditions(data) { | ||||
| 	const rewardsPerCondition = {}; | ||||
| 	await db.delete('conditions:active'); | ||||
| 	const conditions = []; | ||||
|  | ||||
| 	data.forEach(function (reward) { | ||||
| 		conditions.push(reward.condition); | ||||
| 		rewardsPerCondition[reward.condition] = rewardsPerCondition[reward.condition] || []; | ||||
| 		rewardsPerCondition[reward.condition].push(reward.id); | ||||
| 	}); | ||||
|  | ||||
| 	await db.setAdd('conditions:active', conditions); | ||||
|  | ||||
| 	await Promise.all(Object.keys(rewardsPerCondition).map(c => db.setAdd('condition:' + c + ':rewards', rewardsPerCondition[c]))); | ||||
| } | ||||
|  | ||||
| async function getActiveRewards() { | ||||
| 	async function load(id) { | ||||
| 		const [main, rewards] = await Promise.all([ | ||||
| 			db.getObject('rewards:id:' + id), | ||||
| 			db.getObject('rewards:id:' + id + ':rewards'), | ||||
| 		]); | ||||
| 		if (main) { | ||||
| 			main.disabled = main.disabled === 'true'; | ||||
| 			main.rewards = rewards; | ||||
| 		} | ||||
| 		return main; | ||||
| 	} | ||||
|  | ||||
| 	const rewardsList = await db.getSetMembers('rewards:list'); | ||||
| 	const rewardData = await Promise.all(rewardsList.map(id => load(id))); | ||||
| 	return rewardData.filter(Boolean); | ||||
| } | ||||
|  | ||||
| require('../promisify')(rewards); | ||||
|   | ||||
| @@ -1,123 +1,78 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| const util = require('util'); | ||||
|  | ||||
| var async = require('async'); | ||||
| var db = require('../database'); | ||||
| var plugins = require('../plugins'); | ||||
| const db = require('../database'); | ||||
| const plugins = require('../plugins'); | ||||
|  | ||||
| var rewards = module.exports; | ||||
| const rewards = module.exports; | ||||
|  | ||||
| rewards.checkConditionAndRewardUser = function (uid, condition, method, callback) { | ||||
| 	callback = callback || function () {}; | ||||
|  | ||||
| 	async.waterfall([ | ||||
| 		function (next) { | ||||
| 			isConditionActive(condition, next); | ||||
| 		}, | ||||
| 		function (isActive, next) { | ||||
| 			if (!isActive) { | ||||
| 				return callback(); | ||||
| 			} | ||||
| 			getIDsByCondition(condition, next); | ||||
| 		}, | ||||
| 		function (ids, next) { | ||||
| 			getRewardDataByIDs(ids, next); | ||||
| 		}, | ||||
| 		function (rewards, next) { | ||||
| 			filterCompletedRewards(uid, rewards, next); | ||||
| 		}, | ||||
| 		function (rewards, next) { | ||||
| 			if (!rewards || !rewards.length) { | ||||
| 				return callback(); | ||||
| 			} | ||||
|  | ||||
| 			async.filter(rewards, function (reward, next) { | ||||
| 				if (!reward) { | ||||
| 					return next(null, false); | ||||
| 				} | ||||
|  | ||||
| 				checkCondition(reward, method, next); | ||||
| 			}, function (err, eligible) { | ||||
| 				if (err || !eligible) { | ||||
| 					return next(false); | ||||
| 				} | ||||
|  | ||||
| 				giveRewards(uid, eligible, next); | ||||
| 			}); | ||||
| 		}, | ||||
| 	], callback); | ||||
| rewards.checkConditionAndRewardUser = async function (uid, condition, method) { | ||||
| 	const isActive = await isConditionActive(condition); | ||||
| 	if (!isActive) { | ||||
| 		return; | ||||
| 	} | ||||
| 	const ids = await getIDsByCondition(condition); | ||||
| 	let rewardData = await getRewardDataByIDs(ids); | ||||
| 	rewardData = await filterCompletedRewards(uid, rewardData); | ||||
| 	rewardData = rewardData.filter(Boolean); | ||||
| 	if (!rewardData || !rewardData.length) { | ||||
| 		return; | ||||
| 	} | ||||
| 	const eligible = await Promise.all(rewardData.map(reward => checkCondition(reward, method))); | ||||
| 	const eligibleRewards = rewardData.filter((reward, index) => eligible[index]); | ||||
| 	await giveRewards(uid, eligibleRewards); | ||||
| }; | ||||
|  | ||||
| function isConditionActive(condition, callback) { | ||||
| 	db.isSetMember('conditions:active', condition, callback); | ||||
| async function isConditionActive(condition) { | ||||
| 	return await db.isSetMember('conditions:active', condition); | ||||
| } | ||||
|  | ||||
| function getIDsByCondition(condition, callback) { | ||||
| 	db.getSetMembers('condition:' + condition + ':rewards', callback); | ||||
| async function getIDsByCondition(condition) { | ||||
| 	return await db.getSetMembers('condition:' + condition + ':rewards'); | ||||
| } | ||||
|  | ||||
| function filterCompletedRewards(uid, rewards, callback) { | ||||
| 	async.waterfall([ | ||||
| 		function (next) { | ||||
| 			db.getSortedSetRangeByScoreWithScores('uid:' + uid + ':rewards', 0, -1, 1, '+inf', next); | ||||
| 		}, | ||||
| 		function (data, next) { | ||||
| 			var userRewards = {}; | ||||
| async function filterCompletedRewards(uid, rewards) { | ||||
| 	const data = await db.getSortedSetRangeByScoreWithScores('uid:' + uid + ':rewards', 0, -1, 1, '+inf'); | ||||
| 	const userRewards = {}; | ||||
|  | ||||
| 			data.forEach(function (obj) { | ||||
| 				userRewards[obj.value] = parseInt(obj.score, 10); | ||||
| 			}); | ||||
| 	data.forEach(function (obj) { | ||||
| 		userRewards[obj.value] = parseInt(obj.score, 10); | ||||
| 	}); | ||||
|  | ||||
| 			rewards = rewards.filter(function (reward) { | ||||
| 				if (!reward) { | ||||
| 					return false; | ||||
| 				} | ||||
| 	return rewards.filter(function (reward) { | ||||
| 		if (!reward) { | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 				var claimable = parseInt(reward.claimable, 10); | ||||
| 				return claimable === 0 || (!userRewards[reward.id] || userRewards[reward.id] < reward.claimable); | ||||
| 			}); | ||||
|  | ||||
| 			next(null, rewards); | ||||
| 		}, | ||||
| 	], callback); | ||||
| 		const claimable = parseInt(reward.claimable, 10); | ||||
| 		return claimable === 0 || (!userRewards[reward.id] || userRewards[reward.id] < reward.claimable); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function getRewardDataByIDs(ids, callback) { | ||||
| 	db.getObjects(ids.map(function (id) { | ||||
| 		return 'rewards:id:' + id; | ||||
| 	}), callback); | ||||
| async function getRewardDataByIDs(ids) { | ||||
| 	return await db.getObjects(ids.map(id => 'rewards:id:' + id)); | ||||
| } | ||||
|  | ||||
| function getRewardsByRewardData(rewards, callback) { | ||||
| 	db.getObjects(rewards.map(function (reward) { | ||||
| 		return 'rewards:id:' + reward.id + ':rewards'; | ||||
| 	}), callback); | ||||
| async function getRewardsByRewardData(rewards) { | ||||
| 	return await db.getObjects(rewards.map(reward => 'rewards:id:' + reward.id + ':rewards')); | ||||
| } | ||||
|  | ||||
| function checkCondition(reward, method, callback) { | ||||
| 	async.waterfall([ | ||||
| 		function (next) { | ||||
| 			method(next); | ||||
| 		}, | ||||
| 		function (value, next) { | ||||
| 			plugins.fireHook('filter:rewards.checkConditional:' + reward.conditional, { left: value, right: reward.value }, next); | ||||
| 		}, | ||||
| 		function (bool, next) { | ||||
| 			next(null, bool); | ||||
| 		}, | ||||
| 	], callback); | ||||
| async function checkCondition(reward, method) { | ||||
| 	if (method.constructor && method.constructor.name !== 'AsyncFunction') { | ||||
| 		method = util.promisify(method); | ||||
| 	} | ||||
| 	const value = await method(); | ||||
| 	const bool = await plugins.fireHook('filter:rewards.checkConditional:' + reward.conditional, { left: value, right: reward.value }); | ||||
| 	return bool; | ||||
| } | ||||
|  | ||||
| function giveRewards(uid, rewards, callback) { | ||||
| 	async.waterfall([ | ||||
| 		function (next) { | ||||
| 			getRewardsByRewardData(rewards, next); | ||||
| 		}, | ||||
| 		function (rewardData, next) { | ||||
| 			async.each(rewards, function (reward, next) { | ||||
| 				plugins.fireHook('action:rewards.award:' + reward.rid, { uid: uid, reward: rewardData[rewards.indexOf(reward)] }); | ||||
| 				db.sortedSetIncrBy('uid:' + uid + ':rewards', 1, reward.id, next); | ||||
| 			}, next); | ||||
| 		}, | ||||
| 	], callback); | ||||
| async function giveRewards(uid, rewards) { | ||||
| 	const rewardData = await getRewardsByRewardData(rewards); | ||||
| 	await Promise.all(rewards.map(async function (reward) { | ||||
| 		plugins.fireHook('action:rewards.award:' + reward.rid, { uid: uid, reward: rewardData[rewards.indexOf(reward)] }); | ||||
| 		await db.sortedSetIncrBy('uid:' + uid + ':rewards', 1, reward.id); | ||||
| 	})); | ||||
| } | ||||
|  | ||||
| require('../promisify')(rewards); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user