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