mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 11:05:54 +01:00 
			
		
		
		
	feat: convert src/messaging/* to async/await (#7778)
* feat: src/messaging/create.js, #7743 * feat: src/messaging/data.js, #7743 * feat: src/messaging/delete.js, #7743 * feat: src/messaging/edit.js, index.js, #7743 * fix: added in missing awaits * feat: wrapped up src/messaging/* rewrite * refactor: messaging delete/restore similar blocks of code
This commit is contained in:
		| @@ -1,120 +1,78 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
| var async = require('async'); |  | ||||||
|  |  | ||||||
| var meta = require('../meta'); | var meta = require('../meta'); | ||||||
| var plugins = require('../plugins'); | var plugins = require('../plugins'); | ||||||
| var db = require('../database'); | var db = require('../database'); | ||||||
| var user = require('../user'); | var user = require('../user'); | ||||||
|  |  | ||||||
| module.exports = function (Messaging) { | module.exports = function (Messaging) { | ||||||
| 	Messaging.sendMessage = function (data, callback) { | 	Messaging.sendMessage = async (data) => { | ||||||
| 		async.waterfall([ | 		await Messaging.checkContent(data.content); | ||||||
| 			function (next) { | 		const inRoom = await Messaging.isUserInRoom(data.uid, data.roomId); | ||||||
| 				Messaging.checkContent(data.content, next); | 		if (!inRoom) { | ||||||
| 			}, | 			throw new Error('[[error:not-allowed]]'); | ||||||
| 			function (next) { |  | ||||||
| 				Messaging.isUserInRoom(data.uid, data.roomId, next); |  | ||||||
| 			}, |  | ||||||
| 			function (inRoom, next) { |  | ||||||
| 				if (!inRoom) { |  | ||||||
| 					return next(new Error('[[error:not-allowed]]')); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				Messaging.addMessage(data, next); |  | ||||||
| 			}, |  | ||||||
| 		], callback); |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	Messaging.checkContent = function (content, callback) { |  | ||||||
| 		if (!content) { |  | ||||||
| 			return callback(new Error('[[error:invalid-chat-message]]')); |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		plugins.fireHook('filter:messaging.checkContent', { content: content }, function (err, data) { | 		return await Messaging.addMessage(data); | ||||||
| 			if (err) { |  | ||||||
| 				return callback(err); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			content = String(data.content).trim(); |  | ||||||
| 			if (!content) { |  | ||||||
| 				return callback(new Error('[[error:invalid-chat-message]]')); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			var maximumChatMessageLength = (meta.config.maximumChatMessageLength || 1000); |  | ||||||
| 			if (content.length > maximumChatMessageLength) { |  | ||||||
| 				return callback(new Error('[[error:chat-message-too-long, ' + maximumChatMessageLength + ']]')); |  | ||||||
| 			} |  | ||||||
| 			callback(); |  | ||||||
| 		}); |  | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	Messaging.addMessage = function (data, callback) { | 	Messaging.checkContent = async (content) => { | ||||||
| 		var mid; | 		if (!content) { | ||||||
| 		var message; | 			throw new Error('[[error:invalid-chat-message]]'); | ||||||
| 		var isNewSet; | 		} | ||||||
|  |  | ||||||
|  | 		const maximumChatMessageLength = (meta.config.maximumChatMessageLength || 1000); | ||||||
|  | 		const data = await plugins.fireHook('filter:messaging.checkContent', { content: content }); | ||||||
|  | 		content = String(data.content).trim(); | ||||||
|  | 		if (!content) { | ||||||
|  | 			throw new Error('[[error:invalid-chat-message]]'); | ||||||
|  | 		} | ||||||
|  | 		if (content.length > maximumChatMessageLength) { | ||||||
|  | 			throw new Error('[[error:chat-message-too-long, ' + maximumChatMessageLength + ']]'); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	Messaging.addMessage = async (data) => { | ||||||
|  | 		const mid = await db.incrObjectField('global', 'nextMid'); | ||||||
| 		const timestamp = data.timestamp || new Date().getTime(); | 		const timestamp = data.timestamp || new Date().getTime(); | ||||||
|  | 		let message = { | ||||||
|  | 			content: String(data.content), | ||||||
|  | 			timestamp: timestamp, | ||||||
|  | 			fromuid: data.uid, | ||||||
|  | 			roomId: data.roomId, | ||||||
|  | 			deleted: 0, | ||||||
|  | 			system: data.system || 0, | ||||||
|  | 		}; | ||||||
|  |  | ||||||
| 		async.waterfall([ | 		if (data.ip) { | ||||||
| 			function (next) { | 			message.ip = data.ip; | ||||||
| 				Messaging.checkContent(data.content, next); | 		} | ||||||
| 			}, |  | ||||||
| 			function (next) { |  | ||||||
| 				db.incrObjectField('global', 'nextMid', next); |  | ||||||
| 			}, |  | ||||||
| 			function (_mid, next) { |  | ||||||
| 				mid = _mid; |  | ||||||
| 				message = { |  | ||||||
| 					content: String(data.content), |  | ||||||
| 					timestamp: timestamp, |  | ||||||
| 					fromuid: data.uid, |  | ||||||
| 					roomId: data.roomId, |  | ||||||
| 					deleted: 0, |  | ||||||
| 					system: data.system || 0, |  | ||||||
| 				}; |  | ||||||
| 				if (data.ip) { |  | ||||||
| 					message.ip = data.ip; |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				plugins.fireHook('filter:messaging.save', message, next); | 		message = await plugins.fireHook('filter:messaging.save', message); | ||||||
| 			}, | 		await db.setObject('message:' + mid, message); | ||||||
| 			function (message, next) { | 		const isNewSet = await Messaging.isNewSet(data.uid, data.roomId, timestamp); | ||||||
| 				db.setObject('message:' + mid, message, next); | 		let uids = await db.getSortedSetRange('chat:room:' + data.roomId + ':uids', 0, -1); | ||||||
| 			}, | 		uids = await user.blocks.filterUids(data.uid, uids); | ||||||
| 			function (next) { |  | ||||||
| 				Messaging.isNewSet(data.uid, data.roomId, timestamp, next); |  | ||||||
| 			}, |  | ||||||
| 			function (_isNewSet, next) { |  | ||||||
| 				isNewSet = _isNewSet; |  | ||||||
| 				db.getSortedSetRange('chat:room:' + data.roomId + ':uids', 0, -1, next); |  | ||||||
| 			}, |  | ||||||
| 			function (uids, next) { |  | ||||||
| 				user.blocks.filterUids(data.uid, uids, next); |  | ||||||
| 			}, |  | ||||||
| 			function (uids, next) { |  | ||||||
| 				async.parallel([ |  | ||||||
| 					async.apply(Messaging.addRoomToUsers, data.roomId, uids, timestamp), |  | ||||||
| 					async.apply(Messaging.addMessageToUsers, data.roomId, uids, mid, timestamp), |  | ||||||
| 					async.apply(Messaging.markUnread, uids, data.roomId), |  | ||||||
| 				], next); |  | ||||||
| 			}, |  | ||||||
| 			function (results, next) { |  | ||||||
| 				async.parallel({ |  | ||||||
| 					markRead: async.apply(Messaging.markRead, data.uid, data.roomId), |  | ||||||
| 					messages: async.apply(Messaging.getMessagesData, [mid], data.uid, data.roomId, true), |  | ||||||
| 				}, next); |  | ||||||
| 			}, |  | ||||||
| 			function (results, next) { |  | ||||||
| 				if (!results.messages || !results.messages[0]) { |  | ||||||
| 					return next(null, null); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				results.messages[0].newSet = isNewSet; | 		await Promise.all([ | ||||||
| 				results.messages[0].mid = mid; | 			Messaging.addRoomToUsers(data.roomId, uids, timestamp), | ||||||
| 				results.messages[0].roomId = data.roomId; | 			Messaging.addMessageToUsers(data.roomId, uids, mid, timestamp), | ||||||
| 				next(null, results.messages[0]); | 			Messaging.markUnread(uids, data.roomId), | ||||||
| 			}, | 		]); | ||||||
| 		], callback); |  | ||||||
|  | 		const [, messages] = await Promise.all([ | ||||||
|  | 			await Messaging.markRead(data.uid, data.roomId), | ||||||
|  | 			await Messaging.getMessagesData([mid], data.uid, data.roomId, true), | ||||||
|  | 		]); | ||||||
|  |  | ||||||
|  | 		if (!messages || !messages[0]) { | ||||||
|  | 			return null; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		messages[0].newSet = isNewSet; | ||||||
|  | 		messages[0].mid = mid; | ||||||
|  | 		messages[0].roomId = data.roomId; | ||||||
|  | 		return messages[0]; | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	Messaging.addSystemMessage = async (content, uid, roomId) => { | 	Messaging.addSystemMessage = async (content, uid, roomId) => { | ||||||
| @@ -127,19 +85,20 @@ module.exports = function (Messaging) { | |||||||
| 		Messaging.notifyUsersInRoom(uid, roomId, message); | 		Messaging.notifyUsersInRoom(uid, roomId, message); | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	Messaging.addRoomToUsers = function (roomId, uids, timestamp, callback) { | 	Messaging.addRoomToUsers = async (roomId, uids, timestamp) => { | ||||||
| 		if (!uids.length) { | 		if (!uids.length) { | ||||||
| 			return callback(); | 			return; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		const keys = uids.map(uid => 'uid:' + uid + ':chat:rooms'); | 		const keys = uids.map(uid => 'uid:' + uid + ':chat:rooms'); | ||||||
| 		db.sortedSetsAdd(keys, timestamp, roomId, callback); | 		await db.sortedSetsAdd(keys, timestamp, roomId); | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	Messaging.addMessageToUsers = function (roomId, uids, mid, timestamp, callback) { | 	Messaging.addMessageToUsers = async (roomId, uids, mid, timestamp) => { | ||||||
| 		if (!uids.length) { | 		if (!uids.length) { | ||||||
| 			return callback(); | 			return; | ||||||
| 		} | 		} | ||||||
| 		const keys = uids.map(uid => 'uid:' + uid + ':chat:room:' + roomId + ':mids'); | 		const keys = uids.map(uid => 'uid:' + uid + ':chat:room:' + roomId + ':mids'); | ||||||
| 		db.sortedSetsAdd(keys, timestamp, mid, callback); | 		await db.sortedSetsAdd(keys, timestamp, mid); | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,7 +1,5 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
| var async = require('async'); |  | ||||||
|  |  | ||||||
| var db = require('../database'); | var db = require('../database'); | ||||||
| var user = require('../user'); | var user = require('../user'); | ||||||
| var utils = require('../utils'); | var utils = require('../utils'); | ||||||
| @@ -12,157 +10,126 @@ const intFields = ['timestamp', 'edited', 'fromuid', 'roomId', 'deleted', 'syste | |||||||
| module.exports = function (Messaging) { | module.exports = function (Messaging) { | ||||||
| 	Messaging.newMessageCutoff = 1000 * 60 * 3; | 	Messaging.newMessageCutoff = 1000 * 60 * 3; | ||||||
|  |  | ||||||
| 	Messaging.getMessagesFields = function (mids, fields, callback) { | 	Messaging.getMessagesFields = async (mids, fields) => { | ||||||
| 		if (!Array.isArray(mids) || !mids.length) { | 		if (!Array.isArray(mids) || !mids.length) { | ||||||
| 			return callback(null, []); | 			return []; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		async.waterfall([ | 		const keys = mids.map(mid => 'message:' + mid); | ||||||
| 			function (next) { | 		let messages; | ||||||
| 				const keys = mids.map(mid => 'message:' + mid); | 		if (fields.length) { | ||||||
| 				if (fields.length) { | 			messages = await db.getObjectsFields(keys, fields); | ||||||
| 					db.getObjectsFields(keys, fields, next); | 		} else { | ||||||
| 				} else { | 			messages = await db.getObjects(keys); | ||||||
| 					db.getObjects(keys, next); | 		} | ||||||
|  |  | ||||||
|  | 		messages.forEach(message => modifyMessage(message, fields)); | ||||||
|  | 		return messages; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	Messaging.getMessageField = async (mid, field) => { | ||||||
|  | 		const fields = await Messaging.getMessageFields(mid, [field]); | ||||||
|  | 		return fields ? fields[field] : null; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	Messaging.getMessageFields = async (mid, fields) => { | ||||||
|  | 		const messages = await Messaging.getMessagesFields([mid], fields); | ||||||
|  | 		return messages ? messages[0] : null; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	Messaging.setMessageField = async (mid, field, content) => { | ||||||
|  | 		await db.setObjectField('message:' + mid, field, content); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	Messaging.setMessageFields = async (mid, data) => { | ||||||
|  | 		await db.setObject('message:' + mid, data); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	Messaging.getMessagesData = async (mids, uid, roomId, isNew) => { | ||||||
|  | 		let messages = await Messaging.getMessagesFields(mids, []); | ||||||
|  | 		messages = await user.blocks.filter(uid, 'fromuid', messages); | ||||||
|  | 		messages = messages | ||||||
|  | 			.map(function (msg, idx) { | ||||||
|  | 				if (msg) { | ||||||
|  | 					msg.messageId = parseInt(mids[idx], 10); | ||||||
|  | 					msg.ip = undefined; | ||||||
| 				} | 				} | ||||||
| 			}, | 				return msg; | ||||||
| 			function (messages, next) { | 			}) | ||||||
| 				messages.forEach(message => modifyMessage(message, fields)); | 			.filter(Boolean); | ||||||
| 				next(null, messages); |  | ||||||
| 			}, |  | ||||||
| 		], callback); |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	Messaging.getMessageField = function (mid, field, callback) { | 		const users = await user.getUsersFields( | ||||||
| 		Messaging.getMessageFields(mid, [field], function (err, fields) { | 			messages.map(msg => msg && msg.fromuid), | ||||||
| 			callback(err, fields ? fields[field] : null); | 			['uid', 'username', 'userslug', 'picture', 'status', 'banned'] | ||||||
|  | 		); | ||||||
|  |  | ||||||
|  | 		messages.forEach(function (message, index) { | ||||||
|  | 			message.fromUser = users[index]; | ||||||
|  | 			message.fromUser.banned = !!message.fromUser.banned; | ||||||
|  | 			message.fromUser.deleted = message.fromuid !== message.fromUser.uid && message.fromUser.uid === 0; | ||||||
|  |  | ||||||
|  | 			var self = message.fromuid === parseInt(uid, 10); | ||||||
|  | 			message.self = self ? 1 : 0; | ||||||
|  |  | ||||||
|  | 			message.newSet = false; | ||||||
|  | 			message.roomId = String(message.roomId || roomId); | ||||||
|  | 			message.deleted = !!message.deleted; | ||||||
|  | 			message.system = !!message.system; | ||||||
| 		}); | 		}); | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	Messaging.getMessageFields = function (mid, fields, callback) { | 		messages = await Promise.all(messages.map(async (message) => { | ||||||
| 		Messaging.getMessagesFields([mid], fields, function (err, messages) { | 			if (message.system) { | ||||||
| 			callback(err, messages ? messages[0] : null); | 				return message; | ||||||
| 		}); | 			} | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	Messaging.setMessageField = function (mid, field, content, callback) { | 			const result = await Messaging.parse(message.content, message.fromuid, uid, roomId, isNew); | ||||||
| 		db.setObjectField('message:' + mid, field, content, callback); | 			message.content = result; | ||||||
| 	}; | 			message.cleanedContent = utils.stripHTMLTags(utils.decodeHTMLEntities(result)); | ||||||
|  | 			return message; | ||||||
|  | 		})); | ||||||
|  |  | ||||||
| 	Messaging.setMessageFields = function (mid, data, callback) { | 		if (messages.length > 1) { | ||||||
| 		db.setObject('message:' + mid, data, callback); | 			// Add a spacer in between messages with time gaps between them | ||||||
| 	}; | 			messages = messages.map(function (message, index) { | ||||||
|  | 				// Compare timestamps with the previous message, and check if a spacer needs to be added | ||||||
| 	Messaging.getMessagesData = function (mids, uid, roomId, isNew, callback) { | 				if (index > 0 && message.timestamp > messages[index - 1].timestamp + Messaging.newMessageCutoff) { | ||||||
| 		var messages; | 					// If it's been 5 minutes, this is a new set of messages | ||||||
|  | 					message.newSet = true; | ||||||
| 		async.waterfall([ | 				} else if (index > 0 && message.fromuid !== messages[index - 1].fromuid) { | ||||||
| 			function (next) { | 					// If the previous message was from the other person, this is also a new set | ||||||
| 				Messaging.getMessagesFields(mids, [], next); | 					message.newSet = true; | ||||||
| 			}, |  | ||||||
| 			async.apply(user.blocks.filter, uid, 'fromuid'), |  | ||||||
| 			function (_messages, next) { |  | ||||||
| 				messages = _messages.map(function (msg, idx) { |  | ||||||
| 					if (msg) { |  | ||||||
| 						msg.messageId = parseInt(mids[idx], 10); |  | ||||||
| 						msg.ip = undefined; |  | ||||||
| 					} |  | ||||||
| 					return msg; |  | ||||||
| 				}).filter(Boolean); |  | ||||||
|  |  | ||||||
| 				const uids = messages.map(msg => msg && msg.fromuid); |  | ||||||
|  |  | ||||||
| 				user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'status', 'banned'], next); |  | ||||||
| 			}, |  | ||||||
| 			function (users, next) { |  | ||||||
| 				messages.forEach(function (message, index) { |  | ||||||
| 					message.fromUser = users[index]; |  | ||||||
| 					message.fromUser.banned = !!message.fromUser.banned; |  | ||||||
| 					message.fromUser.deleted = message.fromuid !== message.fromUser.uid && message.fromUser.uid === 0; |  | ||||||
|  |  | ||||||
| 					var self = message.fromuid === parseInt(uid, 10); |  | ||||||
| 					message.self = self ? 1 : 0; |  | ||||||
|  |  | ||||||
| 					message.newSet = false; |  | ||||||
| 					message.roomId = String(message.roomId || roomId); |  | ||||||
| 					message.deleted = !!message.deleted; |  | ||||||
| 					message.system = !!message.system; |  | ||||||
| 				}); |  | ||||||
|  |  | ||||||
| 				async.map(messages, function (message, next) { |  | ||||||
| 					if (message.system) { |  | ||||||
| 						return setImmediate(next, null, message); |  | ||||||
| 					} |  | ||||||
|  |  | ||||||
| 					Messaging.parse(message.content, message.fromuid, uid, roomId, isNew, function (err, result) { |  | ||||||
| 						if (err) { |  | ||||||
| 							return next(err); |  | ||||||
| 						} |  | ||||||
| 						message.content = result; |  | ||||||
| 						message.cleanedContent = utils.stripHTMLTags(utils.decodeHTMLEntities(result)); |  | ||||||
| 						next(null, message); |  | ||||||
| 					}); |  | ||||||
| 				}, next); |  | ||||||
| 			}, |  | ||||||
| 			function (messages, next) { |  | ||||||
| 				if (messages.length > 1) { |  | ||||||
| 					// Add a spacer in between messages with time gaps between them |  | ||||||
| 					messages = messages.map(function (message, index) { |  | ||||||
| 						// Compare timestamps with the previous message, and check if a spacer needs to be added |  | ||||||
| 						if (index > 0 && message.timestamp > messages[index - 1].timestamp + Messaging.newMessageCutoff) { |  | ||||||
| 							// If it's been 5 minutes, this is a new set of messages |  | ||||||
| 							message.newSet = true; |  | ||||||
| 						} else if (index > 0 && message.fromuid !== messages[index - 1].fromuid) { |  | ||||||
| 							// If the previous message was from the other person, this is also a new set |  | ||||||
| 							message.newSet = true; |  | ||||||
| 						} |  | ||||||
|  |  | ||||||
| 						return message; |  | ||||||
| 					}); |  | ||||||
|  |  | ||||||
| 					next(undefined, messages); |  | ||||||
| 				} else if (messages.length === 1) { |  | ||||||
| 					// For single messages, we don't know the context, so look up the previous message and compare |  | ||||||
| 					var key = 'uid:' + uid + ':chat:room:' + roomId + ':mids'; |  | ||||||
| 					async.waterfall([ |  | ||||||
| 						async.apply(db.sortedSetRank, key, messages[0].messageId), |  | ||||||
| 						function (index, next) { |  | ||||||
| 							// Continue only if this isn't the first message in sorted set |  | ||||||
| 							if (index > 0) { |  | ||||||
| 								db.getSortedSetRange(key, index - 1, index - 1, next); |  | ||||||
| 							} else { |  | ||||||
| 								messages[0].newSet = true; |  | ||||||
| 								return next(undefined, messages); |  | ||||||
| 							} |  | ||||||
| 						}, |  | ||||||
| 						function (mid, next) { |  | ||||||
| 							Messaging.getMessageFields(mid, ['fromuid', 'timestamp'], next); |  | ||||||
| 						}, |  | ||||||
| 						function (fields, next) { |  | ||||||
| 							if ((messages[0].timestamp > fields.timestamp + Messaging.newMessageCutoff) || |  | ||||||
| 								(messages[0].fromuid !== fields.fromuid)) { |  | ||||||
| 								// If it's been 5 minutes, this is a new set of messages |  | ||||||
| 								messages[0].newSet = true; |  | ||||||
| 							} |  | ||||||
| 							next(null, messages); |  | ||||||
| 						}, |  | ||||||
| 					], next); |  | ||||||
| 				} else { |  | ||||||
| 					next(null, []); |  | ||||||
| 				} | 				} | ||||||
| 			}, |  | ||||||
| 			function (messages, next) { | 				return message; | ||||||
| 				plugins.fireHook('filter:messaging.getMessages', { | 			}); | ||||||
| 					messages: messages, | 		} else if (messages.length === 1) { | ||||||
| 					uid: uid, | 			// For single messages, we don't know the context, so look up the previous message and compare | ||||||
| 					roomId: roomId, | 			var key = 'uid:' + uid + ':chat:room:' + roomId + ':mids'; | ||||||
| 					isNew: isNew, | 			const index = await db.sortedSetRank(key, messages[0].messageId); | ||||||
| 					mids: mids, | 			if (index > 0) { | ||||||
| 				}, function (err, data) { | 				const mid = await db.getSortedSetRange(key, index - 1, index - 1); | ||||||
| 					next(err, data && data.messages); | 				const fields = await Messaging.getMessageFields(mid, ['fromuid', 'timestamp']); | ||||||
| 				}); | 				if ((messages[0].timestamp > fields.timestamp + Messaging.newMessageCutoff) || | ||||||
| 			}, | 					(messages[0].fromuid !== fields.fromuid)) { | ||||||
| 		], callback); | 					// If it's been 5 minutes, this is a new set of messages | ||||||
|  | 					messages[0].newSet = true; | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				messages[0].newSet = true; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			messages = []; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		const data = await plugins.fireHook('filter:messaging.getMessages', { | ||||||
|  | 			messages: messages, | ||||||
|  | 			uid: uid, | ||||||
|  | 			roomId: roomId, | ||||||
|  | 			isNew: isNew, | ||||||
|  | 			mids: mids, | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		return data && data.messages; | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,31 +1,16 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
| var async = require('async'); |  | ||||||
|  |  | ||||||
| module.exports = function (Messaging) { | module.exports = function (Messaging) { | ||||||
| 	Messaging.deleteMessage = function (mid, roomId, callback) { | 	Messaging.deleteMessage = async mid => await doDeleteRestore(mid, 1); | ||||||
| 		async.waterfall([ | 	Messaging.restoreMessage = async mid => await doDeleteRestore(mid, 0); | ||||||
| 			async.apply(Messaging.getMessageField, mid, 'deleted'), |  | ||||||
| 			function (deleted, next) { |  | ||||||
| 				if (deleted) { |  | ||||||
| 					return next(new Error('[[error:chat-deleted-already]]')); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				Messaging.setMessageField(mid, 'deleted', 1, next); | 	async function doDeleteRestore(mid, state) { | ||||||
| 			}, | 		const field = state ? 'deleted' : 'restored'; | ||||||
| 		], callback); | 		const cur = await Messaging.getMessageField(mid, 'deleted'); | ||||||
| 	}; | 		if (cur === state) { | ||||||
|  | 			throw new Error('[[error:chat-' + field + '-already]]'); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 	Messaging.restoreMessage = function (mid, roomId, callback) { | 		return await Messaging.setMessageField(mid, 'deleted', state); | ||||||
| 		async.waterfall([ | 	} | ||||||
| 			async.apply(Messaging.getMessageField, mid, 'deleted'), |  | ||||||
| 			function (deleted, next) { |  | ||||||
| 				if (!deleted) { |  | ||||||
| 					return next(new Error('[[error:chat-restored-already]]')); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				Messaging.setMessageField(mid, 'deleted', 0, next); |  | ||||||
| 			}, |  | ||||||
| 		], callback); |  | ||||||
| 	}; |  | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,7 +1,5 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
| var async = require('async'); |  | ||||||
|  |  | ||||||
| var meta = require('../meta'); | var meta = require('../meta'); | ||||||
| var user = require('../user'); | var user = require('../user'); | ||||||
|  |  | ||||||
| @@ -9,52 +7,34 @@ var sockets = require('../socket.io'); | |||||||
|  |  | ||||||
|  |  | ||||||
| module.exports = function (Messaging) { | module.exports = function (Messaging) { | ||||||
| 	Messaging.editMessage = function (uid, mid, roomId, content, callback) { | 	Messaging.editMessage = async (uid, mid, roomId, content) => { | ||||||
| 		var uids; | 		const raw = await Messaging.getMessageField(mid, 'content'); | ||||||
| 		async.waterfall([ | 		if (raw === content) { | ||||||
| 			function (next) { | 			return; | ||||||
| 				Messaging.getMessageField(mid, 'content', next); | 		} | ||||||
| 			}, | 		if (!String(content).trim()) { | ||||||
| 			function (raw, next) { | 			throw new Error('[[error:invalid-chat-message]]'); | ||||||
| 				if (raw === content) { | 		} | ||||||
| 					return callback(); | 		await Messaging.setMessageFields(mid, { | ||||||
| 				} | 			content: content, | ||||||
| 				if (!String(content).trim()) { | 			edited: Date.now(), | ||||||
| 					return callback(new Error('[[error:invalid-chat-message]]')); | 		}); | ||||||
| 				} |  | ||||||
| 				Messaging.setMessageFields(mid, { | 		// Propagate this change to users in the room | ||||||
| 					content: content, | 		const [uids, messages] = await Promise.all([ | ||||||
| 					edited: Date.now(), | 			Messaging.getUidsInRoom(roomId, 0, -1), | ||||||
| 				}, next); | 			Messaging.getMessagesData([mid], uid, roomId, true), | ||||||
| 			}, | 		]); | ||||||
| 			function (next) { |  | ||||||
| 				Messaging.getUidsInRoom(roomId, 0, -1, next); | 		uids.forEach(function (uid) { | ||||||
| 			}, | 			sockets.in('uid_' + uid).emit('event:chats.edit', { | ||||||
| 			function (_uids, next) { | 				messages: messages, | ||||||
| 				uids = _uids; | 			}); | ||||||
| 				Messaging.getMessagesData([mid], uid, roomId, true, next); | 		}); | ||||||
| 			}, |  | ||||||
| 			function (messages, next) { |  | ||||||
| 				uids.forEach(function (uid) { |  | ||||||
| 					sockets.in('uid_' + uid).emit('event:chats.edit', { |  | ||||||
| 						messages: messages, |  | ||||||
| 					}); |  | ||||||
| 				}); |  | ||||||
| 				next(); |  | ||||||
| 			}, |  | ||||||
| 		], callback); |  | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	Messaging.canEdit = function (messageId, uid, callback) { | 	const canEditDelete = async (messageId, uid, type) => { | ||||||
| 		canEditDelete(messageId, uid, 'edit', callback); | 		let durationConfig = ''; | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	Messaging.canDelete = function (messageId, uid, callback) { |  | ||||||
| 		canEditDelete(messageId, uid, 'delete', callback); |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	function canEditDelete(messageId, uid, type, callback) { |  | ||||||
| 		var durationConfig = ''; |  | ||||||
| 		if (type === 'edit') { | 		if (type === 'edit') { | ||||||
| 			durationConfig = 'chatEditDuration'; | 			durationConfig = 'chatEditDuration'; | ||||||
| 		} else if (type === 'delete') { | 		} else if (type === 'delete') { | ||||||
| @@ -62,47 +42,39 @@ module.exports = function (Messaging) { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (meta.config.disableChat) { | 		if (meta.config.disableChat) { | ||||||
| 			return callback(new Error('[[error:chat-disabled]]')); | 			throw new Error('[[error:chat-disabled]]'); | ||||||
| 		} else if (meta.config.disableChatMessageEditing) { | 		} else if (meta.config.disableChatMessageEditing) { | ||||||
| 			return callback(new Error('[[error:chat-message-editing-disabled]]')); | 			throw new Error('[[error:chat-message-editing-disabled]]'); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		async.waterfall([ | 		const userData = await user.getUserFields(uid, ['banned', 'email:confirmed']); | ||||||
| 			function (next) { | 		if (userData.banned) { | ||||||
| 				user.getUserFields(uid, ['banned', 'email:confirmed'], next); | 			throw new Error('[[error:user-banned]]'); | ||||||
| 			}, | 		} | ||||||
| 			function (userData, next) { | 		if (meta.config.requireEmailConfirmation && !userData['email:confirmed']) { | ||||||
| 				if (userData.banned) { | 			throw new Error('[[error:email-not-confirmed]]'); | ||||||
| 					return callback(new Error('[[error:user-banned]]')); | 		} | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				if (meta.config.requireEmailConfirmation && !userData['email:confirmed']) { | 		const [isAdmin, messageData] = await Promise.all([ | ||||||
| 					return callback(new Error('[[error:email-not-confirmed]]')); | 			user.isAdministrator(uid), | ||||||
| 				} | 			Messaging.getMessageFields(messageId, ['fromuid', 'timestamp']), | ||||||
| 				async.parallel({ | 		]); | ||||||
| 					isAdmin: function (next) { |  | ||||||
| 						user.isAdministrator(uid, next); |  | ||||||
| 					}, |  | ||||||
| 					messageData: function (next) { |  | ||||||
| 						Messaging.getMessageFields(messageId, ['fromuid', 'timestamp'], next); |  | ||||||
| 					}, |  | ||||||
| 				}, next); |  | ||||||
| 			}, |  | ||||||
| 			function (results, next) { |  | ||||||
| 				if (results.isAdmin) { |  | ||||||
| 					return callback(); |  | ||||||
| 				} |  | ||||||
| 				var chatConfigDuration = meta.config[durationConfig]; |  | ||||||
| 				if (chatConfigDuration && Date.now() - results.messageData.timestamp > chatConfigDuration * 1000) { |  | ||||||
| 					return callback(new Error('[[error:chat-' + type + '-duration-expired, ' + meta.config[durationConfig] + ']]')); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				if (results.messageData.fromuid === parseInt(uid, 10)) { | 		if (isAdmin) { | ||||||
| 					return callback(); | 			return; | ||||||
| 				} | 		} | ||||||
|  | 		var chatConfigDuration = meta.config[durationConfig]; | ||||||
|  | 		if (chatConfigDuration && Date.now() - messageData.timestamp > chatConfigDuration * 1000) { | ||||||
|  | 			throw new Error('[[error:chat-' + type + '-duration-expired, ' + meta.config[durationConfig] + ']]'); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 				next(new Error('[[error:cant-' + type + '-chat-message]]')); | 		if (messageData.fromuid === parseInt(uid, 10)) { | ||||||
| 			}, | 			return; | ||||||
| 		], callback); | 		} | ||||||
| 	} |  | ||||||
|  | 		throw new Error('[[error:cant-' + type + '-chat-message]]'); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	Messaging.canEdit = async (messageId, uid) => await canEditDelete(messageId, uid, 'edit'); | ||||||
|  | 	Messaging.canDelete = async (messageId, uid) => await canEditDelete(messageId, uid, 'delete'); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,16 +1,15 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
|  |  | ||||||
| var async = require('async'); | const validator = require('validator'); | ||||||
| var validator = require('validator'); |  | ||||||
|  |  | ||||||
| var db = require('../database'); | const db = require('../database'); | ||||||
| var user = require('../user'); | const user = require('../user'); | ||||||
| var plugins = require('../plugins'); | const plugins = require('../plugins'); | ||||||
| var meta = require('../meta'); | const meta = require('../meta'); | ||||||
| var utils = require('../utils'); | const utils = require('../utils'); | ||||||
|  |  | ||||||
| var Messaging = module.exports; | const Messaging = module.exports; | ||||||
|  |  | ||||||
| require('./data')(Messaging); | require('./data')(Messaging); | ||||||
| require('./create')(Messaging); | require('./create')(Messaging); | ||||||
| @@ -21,179 +20,126 @@ require('./unread')(Messaging); | |||||||
| require('./notifications')(Messaging); | require('./notifications')(Messaging); | ||||||
|  |  | ||||||
|  |  | ||||||
| Messaging.getMessages = function (params, callback) { | Messaging.getMessages = async (params) => { | ||||||
| 	var uid = params.uid; | 	const isNew = params.isNew || false; | ||||||
| 	var roomId = params.roomId; | 	const start = params.hasOwnProperty('start') ? params.start : 0; | ||||||
| 	var isNew = params.isNew || false; | 	const stop = parseInt(start, 10) + ((params.count || 50) - 1); | ||||||
| 	var start = params.hasOwnProperty('start') ? params.start : 0; |  | ||||||
| 	var stop = parseInt(start, 10) + ((params.count || 50) - 1); |  | ||||||
|  |  | ||||||
| 	var indices = {}; | 	const indices = {}; | ||||||
| 	async.waterfall([ | 	const ok = await canGet('filter:messaging.canGetMessages', params.callerUid, params.uid); | ||||||
| 		function (next) { | 	if (!ok) { | ||||||
| 			canGet('filter:messaging.canGetMessages', params.callerUid, params.uid, next); | 		return; | ||||||
| 		}, | 	} | ||||||
| 		function (canGet, next) { |  | ||||||
| 			if (!canGet) { |  | ||||||
| 				return callback(null, null); |  | ||||||
| 			} |  | ||||||
| 			db.getSortedSetRevRange('uid:' + uid + ':chat:room:' + roomId + ':mids', start, stop, next); |  | ||||||
| 		}, |  | ||||||
| 		function (mids, next) { |  | ||||||
| 			if (!mids.length) { |  | ||||||
| 				return callback(null, []); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			mids.forEach(function (mid, index) { | 	const mids = await db.getSortedSetRevRange('uid:' + params.uid + ':chat:room:' + params.roomId + ':mids', start, stop); | ||||||
| 				indices[mid] = start + index; | 	if (!mids.length) { | ||||||
| 			}); | 		return []; | ||||||
|  | 	} | ||||||
|  | 	mids.forEach(function (mid, index) { | ||||||
|  | 		indices[mid] = start + index; | ||||||
|  | 	}); | ||||||
|  | 	mids.reverse(); | ||||||
|  |  | ||||||
| 			mids.reverse(); | 	let messageData = await Messaging.getMessagesData(mids, params.uid, params.roomId, isNew); | ||||||
|  | 	messageData.forEach(function (messageData) { | ||||||
|  | 		messageData.index = indices[messageData.messageId.toString()]; | ||||||
|  | 	}); | ||||||
|  |  | ||||||
| 			Messaging.getMessagesData(mids, uid, roomId, isNew, next); | 	// Filter out deleted messages unless you're the sender of said message | ||||||
| 		}, | 	messageData = messageData.filter(function (messageData) { | ||||||
| 		function (messageData, next) { | 		return (!messageData.deleted || messageData.fromuid === parseInt(params.uid, 10)); | ||||||
| 			messageData.forEach(function (messageData) { | 	}); | ||||||
| 				messageData.index = indices[messageData.messageId.toString()]; |  | ||||||
| 			}); |  | ||||||
|  |  | ||||||
| 			// Filter out deleted messages unless you're the sender of said message | 	return messageData; | ||||||
| 			messageData = messageData.filter(function (messageData) { |  | ||||||
| 				return (!messageData.deleted || messageData.fromuid === parseInt(params.uid, 10)); |  | ||||||
| 			}); |  | ||||||
|  |  | ||||||
| 			next(null, messageData); |  | ||||||
| 		}, |  | ||||||
| 	], callback); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| function canGet(hook, callerUid, uid, callback) { | async function canGet(hook, callerUid, uid) { | ||||||
| 	plugins.fireHook(hook, { | 	const data = await plugins.fireHook(hook, { | ||||||
| 		callerUid: callerUid, | 		callerUid: callerUid, | ||||||
| 		uid: uid, | 		uid: uid, | ||||||
| 		canGet: parseInt(callerUid, 10) === parseInt(uid, 10), | 		canGet: parseInt(callerUid, 10) === parseInt(uid, 10), | ||||||
| 	}, function (err, data) { |  | ||||||
| 		callback(err, data ? data.canGet : false); |  | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
|  | 	return data ? data.canGet : false; | ||||||
| } | } | ||||||
|  |  | ||||||
| Messaging.parse = function (message, fromuid, uid, roomId, isNew, callback) { | Messaging.parse = async (message, fromuid, uid, roomId, isNew) => { | ||||||
| 	plugins.fireHook('filter:parse.raw', message, function (err, parsed) { | 	const parsed = await plugins.fireHook('filter:parse.raw', message); | ||||||
| 		if (err) { | 	let messageData = { | ||||||
| 			return callback(err); | 		message: message, | ||||||
|  | 		parsed: parsed, | ||||||
|  | 		fromuid: fromuid, | ||||||
|  | 		uid: uid, | ||||||
|  | 		roomId: roomId, | ||||||
|  | 		isNew: isNew, | ||||||
|  | 		parsedMessage: parsed, | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	messageData = await plugins.fireHook('filter:messaging.parse', messageData); | ||||||
|  | 	return messageData ? messageData.parsedMessage : ''; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Messaging.isNewSet = async (uid, roomId, timestamp) => { | ||||||
|  | 	const setKey = 'uid:' + uid + ':chat:room:' + roomId + ':mids'; | ||||||
|  | 	const messages = await db.getSortedSetRevRangeWithScores(setKey, 0, 0); | ||||||
|  | 	if (messages && messages.length) { | ||||||
|  | 		return parseInt(timestamp, 10) > parseInt(messages[0].score, 10) + Messaging.newMessageCutoff; | ||||||
|  | 	} | ||||||
|  | 	return true; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Messaging.getRecentChats = async (callerUid, uid, start, stop) => { | ||||||
|  | 	const ok = await canGet('filter:messaging.canGetRecentChats', callerUid, uid); | ||||||
|  | 	if (!ok) { | ||||||
|  | 		return null; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	const roomIds = await db.getSortedSetRevRange('uid:' + uid + ':chat:rooms', start, stop); | ||||||
|  | 	const results = await utils.promiseParallel({ | ||||||
|  | 		roomData: Messaging.getRoomsData(roomIds), | ||||||
|  | 		unread: db.isSortedSetMembers('uid:' + uid + ':chat:rooms:unread', roomIds), | ||||||
|  | 		users: Promise.all(roomIds.map(async (roomId) => { | ||||||
|  | 			let uids = await db.getSortedSetRevRange('chat:room:' + roomId + ':uids', 0, 9); | ||||||
|  | 			uids = uids.filter(function (value) { | ||||||
|  | 				return value && parseInt(value, 10) !== parseInt(uid, 10); | ||||||
|  | 			}); | ||||||
|  | 			return await user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'status', 'lastonline']); | ||||||
|  | 		})), | ||||||
|  | 		teasers: Promise.all(roomIds.map(async roomId => Messaging.getTeaser(uid, roomId))), | ||||||
|  | 	}); | ||||||
|  |  | ||||||
|  | 	results.roomData.forEach(function (room, index) { | ||||||
|  | 		if (room) { | ||||||
|  | 			room.users = results.users[index]; | ||||||
|  | 			room.groupChat = room.hasOwnProperty('groupChat') ? room.groupChat : room.users.length > 2; | ||||||
|  | 			room.unread = results.unread[index]; | ||||||
|  | 			room.teaser = results.teasers[index]; | ||||||
|  |  | ||||||
|  | 			room.users.forEach(function (userData) { | ||||||
|  | 				if (userData && parseInt(userData.uid, 10)) { | ||||||
|  | 					userData.status = user.getStatus(userData); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  | 			room.users = room.users.filter(function (user) { | ||||||
|  | 				return user && parseInt(user.uid, 10); | ||||||
|  | 			}); | ||||||
|  | 			room.lastUser = room.users[0]; | ||||||
|  |  | ||||||
|  | 			room.usernames = Messaging.generateUsernames(room.users, uid); | ||||||
| 		} | 		} | ||||||
|  | 	}); | ||||||
|  |  | ||||||
| 		var messageData = { | 	results.roomData = results.roomData.filter(Boolean); | ||||||
| 			message: message, | 	const ref = { rooms: results.roomData, nextStart: stop + 1 }; | ||||||
| 			parsed: parsed, | 	return await plugins.fireHook('filter:messaging.getRecentChats', { | ||||||
| 			fromuid: fromuid, | 		rooms: ref.rooms, | ||||||
| 			uid: uid, | 		nextStart: ref.nextStart, | ||||||
| 			roomId: roomId, | 		uid: uid, | ||||||
| 			isNew: isNew, | 		callerUid: callerUid, | ||||||
| 			parsedMessage: parsed, |  | ||||||
| 		}; |  | ||||||
|  |  | ||||||
| 		plugins.fireHook('filter:messaging.parse', messageData, function (err, messageData) { |  | ||||||
| 			callback(err, messageData ? messageData.parsedMessage : ''); |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Messaging.isNewSet = function (uid, roomId, timestamp, callback) { | Messaging.generateUsernames = (users, excludeUid) => { | ||||||
| 	var setKey = 'uid:' + uid + ':chat:room:' + roomId + ':mids'; |  | ||||||
|  |  | ||||||
| 	async.waterfall([ |  | ||||||
| 		function (next) { |  | ||||||
| 			db.getSortedSetRevRangeWithScores(setKey, 0, 0, next); |  | ||||||
| 		}, |  | ||||||
| 		function (messages, next) { |  | ||||||
| 			if (messages && messages.length) { |  | ||||||
| 				next(null, parseInt(timestamp, 10) > parseInt(messages[0].score, 10) + Messaging.newMessageCutoff); |  | ||||||
| 			} else { |  | ||||||
| 				next(null, true); |  | ||||||
| 			} |  | ||||||
| 		}, |  | ||||||
| 	], callback); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Messaging.getRecentChats = function (callerUid, uid, start, stop, callback) { |  | ||||||
| 	async.waterfall([ |  | ||||||
| 		function (next) { |  | ||||||
| 			canGet('filter:messaging.canGetRecentChats', callerUid, uid, next); |  | ||||||
| 		}, |  | ||||||
| 		function (canGet, next) { |  | ||||||
| 			if (!canGet) { |  | ||||||
| 				return callback(null, null); |  | ||||||
| 			} |  | ||||||
| 			db.getSortedSetRevRange('uid:' + uid + ':chat:rooms', start, stop, next); |  | ||||||
| 		}, |  | ||||||
| 		function (roomIds, next) { |  | ||||||
| 			async.parallel({ |  | ||||||
| 				roomData: function (next) { |  | ||||||
| 					Messaging.getRoomsData(roomIds, next); |  | ||||||
| 				}, |  | ||||||
| 				unread: function (next) { |  | ||||||
| 					db.isSortedSetMembers('uid:' + uid + ':chat:rooms:unread', roomIds, next); |  | ||||||
| 				}, |  | ||||||
| 				users: function (next) { |  | ||||||
| 					async.map(roomIds, function (roomId, next) { |  | ||||||
| 						db.getSortedSetRevRange('chat:room:' + roomId + ':uids', 0, 9, function (err, uids) { |  | ||||||
| 							if (err) { |  | ||||||
| 								return next(err); |  | ||||||
| 							} |  | ||||||
| 							uids = uids.filter(function (value) { |  | ||||||
| 								return value && parseInt(value, 10) !== parseInt(uid, 10); |  | ||||||
| 							}); |  | ||||||
| 							user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'status', 'lastonline'], next); |  | ||||||
| 						}); |  | ||||||
| 					}, next); |  | ||||||
| 				}, |  | ||||||
| 				teasers: function (next) { |  | ||||||
| 					async.map(roomIds, function (roomId, next) { |  | ||||||
| 						Messaging.getTeaser(uid, roomId, next); |  | ||||||
| 					}, next); |  | ||||||
| 				}, |  | ||||||
| 			}, next); |  | ||||||
| 		}, |  | ||||||
| 		function (results, next) { |  | ||||||
| 			results.roomData.forEach(function (room, index) { |  | ||||||
| 				if (room) { |  | ||||||
| 					room.users = results.users[index]; |  | ||||||
| 					room.groupChat = room.hasOwnProperty('groupChat') ? room.groupChat : room.users.length > 2; |  | ||||||
| 					room.unread = results.unread[index]; |  | ||||||
| 					room.teaser = results.teasers[index]; |  | ||||||
|  |  | ||||||
| 					room.users.forEach(function (userData) { |  | ||||||
| 						if (userData && parseInt(userData.uid, 10)) { |  | ||||||
| 							userData.status = user.getStatus(userData); |  | ||||||
| 						} |  | ||||||
| 					}); |  | ||||||
| 					room.users = room.users.filter(function (user) { |  | ||||||
| 						return user && parseInt(user.uid, 10); |  | ||||||
| 					}); |  | ||||||
| 					room.lastUser = room.users[0]; |  | ||||||
|  |  | ||||||
| 					room.usernames = Messaging.generateUsernames(room.users, uid); |  | ||||||
| 				} |  | ||||||
| 			}); |  | ||||||
|  |  | ||||||
| 			results.roomData = results.roomData.filter(Boolean); |  | ||||||
|  |  | ||||||
| 			next(null, { rooms: results.roomData, nextStart: stop + 1 }); |  | ||||||
| 		}, |  | ||||||
| 		function (ref, next) { |  | ||||||
| 			plugins.fireHook('filter:messaging.getRecentChats', { |  | ||||||
| 				rooms: ref.rooms, |  | ||||||
| 				nextStart: ref.nextStart, |  | ||||||
| 				uid: uid, |  | ||||||
| 				callerUid: callerUid, |  | ||||||
| 			}, next); |  | ||||||
| 		}, |  | ||||||
| 	], callback); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| Messaging.generateUsernames = function (users, excludeUid) { |  | ||||||
| 	users = users.filter(function (user) { | 	users = users.filter(function (user) { | ||||||
| 		return user && parseInt(user.uid, 10) !== excludeUid; | 		return user && parseInt(user.uid, 10) !== excludeUid; | ||||||
| 	}); | 	}); | ||||||
| @@ -202,211 +148,149 @@ Messaging.generateUsernames = function (users, excludeUid) { | |||||||
| 	}).join(', '); | 	}).join(', '); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Messaging.getTeaser = function (uid, roomId, callback) { | Messaging.getTeaser = async (uid, roomId) => { | ||||||
| 	var teaser; | 	const mid = await Messaging.getLatestUndeletedMessage(uid, roomId); | ||||||
| 	async.waterfall([ | 	if (!mid) { | ||||||
| 		function (next) { | 		return null; | ||||||
| 			Messaging.getLatestUndeletedMessage(uid, roomId, next); | 	} | ||||||
| 		}, | 	const teaser = await Messaging.getMessageFields(mid, ['fromuid', 'content', 'timestamp']); | ||||||
| 		function (mid, next) { | 	if (!teaser.fromuid) { | ||||||
| 			if (!mid) { | 		return null; | ||||||
| 				return callback(null, null); | 	} | ||||||
| 			} | 	const blocked = await user.blocks.is(teaser.fromuid, uid); | ||||||
| 			Messaging.getMessageFields(mid, ['fromuid', 'content', 'timestamp'], next); | 	if (blocked) { | ||||||
| 		}, | 		return null; | ||||||
| 		function (_teaser, next) { | 	} | ||||||
| 			teaser = _teaser; |  | ||||||
| 			if (!teaser.fromuid) { |  | ||||||
| 				return callback(null, null); |  | ||||||
| 			} |  | ||||||
| 			user.blocks.is(teaser.fromuid, uid, next); |  | ||||||
| 		}, |  | ||||||
| 		function (blocked, next) { |  | ||||||
| 			if (blocked) { |  | ||||||
| 				return callback(null, null); |  | ||||||
| 			} |  | ||||||
| 			if (teaser.content) { |  | ||||||
| 				teaser.content = utils.stripHTMLTags(utils.decodeHTMLEntities(teaser.content)); |  | ||||||
| 				teaser.content = validator.escape(String(teaser.content)); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			teaser.timestampISO = utils.toISOString(teaser.timestamp); | 	teaser.user = await user.getUserFields(teaser.fromuid, ['uid', 'username', 'userslug', 'picture', 'status', 'lastonline']); | ||||||
| 			user.getUserFields(teaser.fromuid, ['uid', 'username', 'userslug', 'picture', 'status', 'lastonline'], next); | 	if (teaser.content) { | ||||||
| 		}, | 		teaser.content = utils.stripHTMLTags(utils.decodeHTMLEntities(teaser.content)); | ||||||
| 		function (user, next) { | 		teaser.content = validator.escape(String(teaser.content)); | ||||||
| 			teaser.user = user; | 	} | ||||||
| 			plugins.fireHook('filter:messaging.getTeaser', { teaser: teaser }, function (err, data) { |  | ||||||
| 				next(err, data.teaser); | 	const payload = await plugins.fireHook('filter:messaging.getTeaser', { teaser: teaser }); | ||||||
| 			}); | 	return payload.teaser; | ||||||
| 		}, |  | ||||||
| 	], callback); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Messaging.getLatestUndeletedMessage = function (uid, roomId, callback) { | Messaging.getLatestUndeletedMessage = async (uid, roomId) => { | ||||||
| 	var done = false; | 	let done = false; | ||||||
| 	var latestMid = null; | 	let latestMid = null; | ||||||
| 	var index = 0; | 	let index = 0; | ||||||
| 	var mids; | 	let mids; | ||||||
| 	async.doWhilst( |  | ||||||
| 		function (next) { | 	while (!done) { | ||||||
| 			async.waterfall([ | 		/* eslint-disable no-await-in-loop */ | ||||||
| 				function (_next) { | 		mids = await db.getSortedSetRevRange('uid:' + uid + ':chat:room:' + roomId + ':mids', index, index); | ||||||
| 					db.getSortedSetRevRange('uid:' + uid + ':chat:room:' + roomId + ':mids', index, index, _next); | 		if (mids.length) { | ||||||
| 				}, | 			const states = await Messaging.getMessageFields(mids[0], ['deleted', 'system']); | ||||||
| 				function (_mids, _next) { | 			done = !states.deleted && !states.system; | ||||||
| 					mids = _mids; | 			if (done) { | ||||||
| 					if (!mids.length) { | 				latestMid = mids[0]; | ||||||
| 						done = true; | 			} | ||||||
| 						return next(); | 			index += 1; | ||||||
| 					} | 		} else { | ||||||
| 					Messaging.getMessageFields(mids[0], ['deleted', 'system'], _next); | 			done = true; | ||||||
| 				}, |  | ||||||
| 				function (states, _next) { |  | ||||||
| 					done = !states.deleted && !states.system; |  | ||||||
| 					if (done) { |  | ||||||
| 						latestMid = mids[0]; |  | ||||||
| 					} |  | ||||||
| 					index += 1; |  | ||||||
| 					_next(); |  | ||||||
| 				}, |  | ||||||
| 			], next); |  | ||||||
| 		}, |  | ||||||
| 		function (next) { |  | ||||||
| 			next(null, !done); |  | ||||||
| 		}, |  | ||||||
| 		function (err) { |  | ||||||
| 			callback(err, parseInt(latestMid, 10)); |  | ||||||
| 		} | 		} | ||||||
| 	); | 	} | ||||||
|  |  | ||||||
|  | 	return latestMid; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Messaging.canMessageUser = function (uid, toUid, callback) { | Messaging.canMessageUser = async (uid, toUid) => { | ||||||
| 	if (meta.config.disableChat || uid <= 0 || uid === toUid) { | 	if (meta.config.disableChat || uid <= 0 || uid === toUid) { | ||||||
| 		return callback(new Error('[[error:chat-disabled]]')); | 		throw new Error('[[error:chat-disabled]]'); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (parseInt(uid, 10) === parseInt(toUid, 10)) { | 	if (parseInt(uid, 10) === parseInt(toUid, 10)) { | ||||||
| 		return callback(new Error('[[error:cant-chat-with-yourself')); | 		throw new Error('[[error:cant-chat-with-yourself'); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	async.waterfall([ | 	const exists = await user.exists(toUid); | ||||||
| 		function (next) { | 	if (!exists) { | ||||||
| 			user.exists(toUid, next); | 		throw new Error('[[error:no-user]]'); | ||||||
| 		}, | 	} | ||||||
| 		function (exists, next) { |  | ||||||
| 			if (!exists) { |  | ||||||
| 				return callback(new Error('[[error:no-user]]')); |  | ||||||
| 			} |  | ||||||
| 			user.getUserFields(uid, ['banned', 'email:confirmed'], next); |  | ||||||
| 		}, |  | ||||||
| 		function (userData, next) { |  | ||||||
| 			if (userData.banned) { |  | ||||||
| 				return callback(new Error('[[error:user-banned]]')); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if (meta.config.requireEmailConfirmation && !userData['email:confirmed']) { | 	const userData = await user.getUserFields(uid, ['banned', 'email:confirmed']); | ||||||
| 				return callback(new Error('[[error:email-not-confirmed-chat]]')); | 	if (userData.banned) { | ||||||
| 			} | 		throw new Error('[[error:user-banned]]'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 			async.parallel({ | 	if (meta.config.requireEmailConfirmation && !userData['email:confirmed']) { | ||||||
| 				settings: async.apply(user.getSettings, toUid), | 		throw new Error('[[error:email-not-confirmed-chat]]'); | ||||||
| 				isAdmin: async.apply(user.isAdministrator, uid), | 	} | ||||||
| 				isModerator: async.apply(user.isModeratorOfAnyCategory, uid), |  | ||||||
| 				isFollowing: async.apply(user.isFollowing, toUid, uid), |  | ||||||
| 			}, next); |  | ||||||
| 		}, |  | ||||||
| 		function (results, next) { |  | ||||||
| 			if (results.settings.restrictChat && !results.isAdmin && !results.isModerator && !results.isFollowing) { |  | ||||||
| 				return next(new Error('[[error:chat-restricted]]')); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			plugins.fireHook('static:messaging.canMessageUser', { | 	const results = await utils.promiseParallel({ | ||||||
| 				uid: uid, | 		settings: user.getSettings(toUid), | ||||||
| 				toUid: toUid, | 		isAdmin: user.isAdministrator(uid), | ||||||
| 			}, function (err) { | 		isModerator: user.isModeratorOfAnyCategory(uid), | ||||||
| 				next(err); | 		isFollowing: user.isFollowing(toUid, uid), | ||||||
| 			}); | 	}); | ||||||
| 		}, |  | ||||||
| 	], callback); | 	if (results.settings.restrictChat && !results.isAdmin && !results.isModerator && !results.isFollowing) { | ||||||
|  | 		throw new Error('[[error:chat-restricted]]'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	await plugins.fireHook('static:messaging.canMessageUser', { | ||||||
|  | 		uid: uid, | ||||||
|  | 		toUid: toUid, | ||||||
|  | 	}); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Messaging.canMessageRoom = function (uid, roomId, callback) { | Messaging.canMessageRoom = async (uid, roomId) => { | ||||||
| 	if (meta.config.disableChat || uid <= 0) { | 	if (meta.config.disableChat || uid <= 0) { | ||||||
| 		return callback(new Error('[[error:chat-disabled]]')); | 		throw new Error('[[error:chat-disabled]]'); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	async.waterfall([ | 	const inRoom = await Messaging.isUserInRoom(uid, roomId); | ||||||
| 		function (next) { | 	if (!inRoom) { | ||||||
| 			Messaging.isUserInRoom(uid, roomId, next); | 		throw new Error('[[error:not-in-room]]'); | ||||||
| 		}, | 	} | ||||||
| 		function (inRoom, next) { |  | ||||||
| 			if (!inRoom) { |  | ||||||
| 				return next(new Error('[[error:not-in-room]]')); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			user.getUserFields(uid, ['banned', 'email:confirmed'], next); | 	const userData = await user.getUserFields(uid, ['banned', 'email:confirmed']); | ||||||
| 		}, | 	if (userData.banned) { | ||||||
| 		function (userData, next) { | 		throw new Error('[[error:user-banned]]'); | ||||||
| 			if (userData.banned) { | 	} | ||||||
| 				return next(new Error('[[error:user-banned]]')); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if (meta.config.requireEmailConfirmation && !userData['email:confirmed']) { | 	if (meta.config.requireEmailConfirmation && !userData['email:confirmed']) { | ||||||
| 				return next(new Error('[[error:email-not-confirmed-chat]]')); | 		throw new Error('[[error:email-not-confirmed-chat]]'); | ||||||
| 			} | 	} | ||||||
|  |  | ||||||
| 			plugins.fireHook('static:messaging.canMessageRoom', { | 	await plugins.fireHook('static:messaging.canMessageRoom', { | ||||||
| 				uid: uid, | 		uid: uid, | ||||||
| 				roomId: roomId, | 		roomId: roomId, | ||||||
| 			}, function (err) { | 	}); | ||||||
| 				next(err); |  | ||||||
| 			}); |  | ||||||
| 		}, |  | ||||||
| 	], callback); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Messaging.hasPrivateChat = function (uid, withUid, callback) { | Messaging.hasPrivateChat = async (uid, withUid) => { | ||||||
| 	if (parseInt(uid, 10) === parseInt(withUid, 10)) { | 	if (parseInt(uid, 10) === parseInt(withUid, 10)) { | ||||||
| 		return callback(null, 0); | 		return 0; | ||||||
| 	} | 	} | ||||||
| 	async.waterfall([ |  | ||||||
| 		function (next) { |  | ||||||
| 			async.parallel({ |  | ||||||
| 				myRooms: async.apply(db.getSortedSetRevRange, 'uid:' + uid + ':chat:rooms', 0, -1), |  | ||||||
| 				theirRooms: async.apply(db.getSortedSetRevRange, 'uid:' + withUid + ':chat:rooms', 0, -1), |  | ||||||
| 			}, next); |  | ||||||
| 		}, |  | ||||||
| 		function (results, next) { |  | ||||||
| 			var roomIds = results.myRooms.filter(function (roomId) { |  | ||||||
| 				return roomId && results.theirRooms.includes(roomId); |  | ||||||
| 			}); |  | ||||||
|  |  | ||||||
| 			if (!roomIds.length) { | 	const results = await utils.promiseParallel({ | ||||||
| 				return callback(); | 		myRooms: db.getSortedSetRevRange('uid:' + uid + ':chat:rooms', 0, -1), | ||||||
| 			} | 		theirRooms: db.getSortedSetRevRange('uid:' + withUid + ':chat:rooms', 0, -1), | ||||||
|  | 	}); | ||||||
|  | 	const roomIds = results.myRooms.filter(function (roomId) { | ||||||
|  | 		return roomId && results.theirRooms.includes(roomId); | ||||||
|  | 	}); | ||||||
|  |  | ||||||
| 			var index = 0; | 	if (!roomIds.length) { | ||||||
| 			var roomId = 0; | 		return 0; | ||||||
| 			async.whilst(function (next) { | 	} | ||||||
| 				next(null, index < roomIds.length && !roomId); |  | ||||||
| 			}, function (next) { | 	var index = 0; | ||||||
| 				Messaging.getUserCountInRoom(roomIds[index], function (err, count) { | 	var roomId = 0; | ||||||
| 					if (err) { | 	while (index < roomIds.length && !roomId) { | ||||||
| 						return next(err); | 		/* eslint-disable no-await-in-loop */ | ||||||
| 					} | 		const count = await Messaging.getUserCountInRoom(roomIds[index]); | ||||||
| 					if (count === 2) { | 		if (count === 2) { | ||||||
| 						roomId = roomIds[index]; | 			roomId = roomIds[index]; | ||||||
| 						next(null, roomId); | 		} else { | ||||||
| 					} else { | 			index += 1; | ||||||
| 						index += 1; | 		} | ||||||
| 						next(); | 	} | ||||||
| 					} |  | ||||||
| 				}); | 	return roomId; | ||||||
| 			}, function (err) { |  | ||||||
| 				next(err, roomId); |  | ||||||
| 			}); |  | ||||||
| 		}, |  | ||||||
| 	], callback); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Messaging.async = require('../promisify')(Messaging); | Messaging.async = require('../promisify')(Messaging); | ||||||
|   | |||||||
| @@ -1,7 +1,5 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
| var async = require('async'); |  | ||||||
|  |  | ||||||
| var user = require('../user'); | var user = require('../user'); | ||||||
| var notifications = require('../notifications'); | var notifications = require('../notifications'); | ||||||
| var sockets = require('../socket.io'); | var sockets = require('../socket.io'); | ||||||
| @@ -12,88 +10,67 @@ module.exports = function (Messaging) { | |||||||
|  |  | ||||||
| 	Messaging.notificationSendDelay = 1000 * 60; | 	Messaging.notificationSendDelay = 1000 * 60; | ||||||
|  |  | ||||||
| 	Messaging.notifyUsersInRoom = function (fromUid, roomId, messageObj) { | 	Messaging.notifyUsersInRoom = async (fromUid, roomId, messageObj) => { | ||||||
| 		async.waterfall([ | 		let uids = await Messaging.getUidsInRoom(roomId, 0, -1); | ||||||
| 			function (next) { | 		uids = await user.blocks.filterUids(fromUid, uids); | ||||||
| 				Messaging.getUidsInRoom(roomId, 0, -1, next); |  | ||||||
| 			}, |  | ||||||
| 			function (uids, next) { |  | ||||||
| 				user.blocks.filterUids(fromUid, uids, next); |  | ||||||
| 			}, |  | ||||||
| 			function (uids, next) { |  | ||||||
| 				var data = { |  | ||||||
| 					roomId: roomId, |  | ||||||
| 					fromUid: fromUid, |  | ||||||
| 					message: messageObj, |  | ||||||
| 					uids: uids, |  | ||||||
| 				}; |  | ||||||
|  |  | ||||||
| 				plugins.fireHook('filter:messaging.notify', data, next); | 		let data = { | ||||||
| 			}, | 			roomId: roomId, | ||||||
| 			function (data, next) { | 			fromUid: fromUid, | ||||||
| 				if (!data || !data.uids || !data.uids.length) { | 			message: messageObj, | ||||||
| 					return next(); | 			uids: uids, | ||||||
| 				} | 		}; | ||||||
|  | 		data = await plugins.fireHook('filter:messaging.notify', data); | ||||||
|  | 		if (!data || !data.uids || !data.uids.length) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 				var uids = data.uids; | 		uids = data.uids; | ||||||
|  | 		uids.forEach(function (uid) { | ||||||
|  | 			data.self = parseInt(uid, 10) === parseInt(fromUid, 10) ? 1 : 0; | ||||||
|  | 			Messaging.pushUnreadCount(uid); | ||||||
|  | 			sockets.in('uid_' + uid).emit('event:chats.receive', data); | ||||||
|  | 		}); | ||||||
|  |  | ||||||
| 				uids.forEach(function (uid) { | 		// Delayed notifications | ||||||
| 					data.self = parseInt(uid, 10) === parseInt(fromUid, 10) ? 1 : 0; | 		var queueObj = Messaging.notifyQueue[fromUid + ':' + roomId]; | ||||||
| 					Messaging.pushUnreadCount(uid); | 		if (queueObj) { | ||||||
| 					sockets.in('uid_' + uid).emit('event:chats.receive', data); | 			queueObj.message.content += '\n' + messageObj.content; | ||||||
| 				}); | 			clearTimeout(queueObj.timeout); | ||||||
|  | 		} else { | ||||||
|  | 			queueObj = { | ||||||
|  | 				message: messageObj, | ||||||
|  | 			}; | ||||||
|  | 			Messaging.notifyQueue[fromUid + ':' + roomId] = queueObj; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 				// Delayed notifications | 		queueObj.timeout = setTimeout(function () { | ||||||
| 				var queueObj = Messaging.notifyQueue[fromUid + ':' + roomId]; | 			sendNotifications(fromUid, uids, roomId, queueObj.message); | ||||||
| 				if (queueObj) { | 		}, Messaging.notificationSendDelay); | ||||||
| 					queueObj.message.content += '\n' + messageObj.content; |  | ||||||
| 					clearTimeout(queueObj.timeout); |  | ||||||
| 				} else { |  | ||||||
| 					queueObj = { |  | ||||||
| 						message: messageObj, |  | ||||||
| 					}; |  | ||||||
| 					Messaging.notifyQueue[fromUid + ':' + roomId] = queueObj; |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				queueObj.timeout = setTimeout(function () { |  | ||||||
| 					sendNotifications(fromUid, uids, roomId, queueObj.message); |  | ||||||
| 				}, Messaging.notificationSendDelay); |  | ||||||
| 				next(); |  | ||||||
| 			}, |  | ||||||
| 		]); |  | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	function sendNotifications(fromuid, uids, roomId, messageObj) { | 	async function sendNotifications(fromuid, uids, roomId, messageObj) { | ||||||
| 		async.waterfall([ | 		const isOnline = await user.isOnline(uids); | ||||||
| 			function (next) { | 		uids = uids.filter(function (uid, index) { | ||||||
| 				user.isOnline(uids, next); | 			return !isOnline[index] && parseInt(fromuid, 10) !== parseInt(uid, 10); | ||||||
| 			}, |  | ||||||
| 			function (isOnline, next) { |  | ||||||
| 				uids = uids.filter(function (uid, index) { |  | ||||||
| 					return !isOnline[index] && parseInt(fromuid, 10) !== parseInt(uid, 10); |  | ||||||
| 				}); |  | ||||||
|  |  | ||||||
| 				if (!uids.length) { |  | ||||||
| 					return; |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				notifications.create({ |  | ||||||
| 					type: 'new-chat', |  | ||||||
| 					subject: '[[email:notif.chat.subject, ' + messageObj.fromUser.username + ']]', |  | ||||||
| 					bodyShort: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', |  | ||||||
| 					bodyLong: messageObj.content, |  | ||||||
| 					nid: 'chat_' + fromuid + '_' + roomId, |  | ||||||
| 					from: fromuid, |  | ||||||
| 					path: '/chats/' + messageObj.roomId, |  | ||||||
| 				}, next); |  | ||||||
| 			}, |  | ||||||
| 		], function (err, notification) { |  | ||||||
| 			if (!err) { |  | ||||||
| 				delete Messaging.notifyQueue[fromuid + ':' + roomId]; |  | ||||||
| 				if (notification) { |  | ||||||
| 					notifications.push(notification, uids); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		}); | 		}); | ||||||
|  | 		if (!uids.length) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		const notification = await notifications.create({ | ||||||
|  | 			type: 'new-chat', | ||||||
|  | 			subject: '[[email:notif.chat.subject, ' + messageObj.fromUser.username + ']]', | ||||||
|  | 			bodyShort: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', | ||||||
|  | 			bodyLong: messageObj.content, | ||||||
|  | 			nid: 'chat_' + fromuid + '_' + roomId, | ||||||
|  | 			from: fromuid, | ||||||
|  | 			path: '/chats/' + messageObj.roomId, | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		delete Messaging.notifyQueue[fromuid + ':' + roomId]; | ||||||
|  | 		if (notification) { | ||||||
|  | 			notifications.push(notification, uids); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,53 +1,37 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
| var async = require('async'); |  | ||||||
|  |  | ||||||
| var db = require('../database'); | var db = require('../database'); | ||||||
| var sockets = require('../socket.io'); | var sockets = require('../socket.io'); | ||||||
|  |  | ||||||
| module.exports = function (Messaging) { | module.exports = function (Messaging) { | ||||||
| 	Messaging.getUnreadCount = function (uid, callback) { | 	Messaging.getUnreadCount = async (uid) => { | ||||||
| 		if (parseInt(uid, 10) <= 0) { | 		if (parseInt(uid, 10) <= 0) { | ||||||
| 			return setImmediate(callback, null, 0); | 			return 0; | ||||||
| 		} | 		} | ||||||
| 		db.sortedSetCard('uid:' + uid + ':chat:rooms:unread', callback); |  | ||||||
|  | 		return await db.sortedSetCard('uid:' + uid + ':chat:rooms:unread'); | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	Messaging.pushUnreadCount = function (uid) { | 	Messaging.pushUnreadCount = async (uid) => { | ||||||
| 		if (parseInt(uid, 10) <= 0) { | 		if (parseInt(uid, 10) <= 0) { | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		Messaging.getUnreadCount(uid, function (err, unreadCount) { | 		const unreadCount = await Messaging.getUnreadCount(uid); | ||||||
| 			if (err) { | 		sockets.in('uid_' + uid).emit('event:unread.updateChatCount', unreadCount); | ||||||
| 				return; | 	}; | ||||||
| 			} |  | ||||||
| 			sockets.in('uid_' + uid).emit('event:unread.updateChatCount', unreadCount); | 	Messaging.markRead = async (uid, roomId) => db.sortedSetRemove('uid:' + uid + ':chat:rooms:unread', roomId); | ||||||
|  | 	Messaging.markAllRead = async uid => db.delete('uid:' + uid + ':chat:rooms:unread'); | ||||||
|  |  | ||||||
|  | 	Messaging.markUnread = async (uids, roomId) => { | ||||||
|  | 		const exists = await Messaging.roomExists(roomId); | ||||||
|  | 		if (!exists) { | ||||||
|  | 			throw new Error('[[error:chat-room-does-not-exist]]'); | ||||||
|  | 		} | ||||||
|  | 		var keys = uids.map(function (uid) { | ||||||
|  | 			return 'uid:' + uid + ':chat:rooms:unread'; | ||||||
| 		}); | 		}); | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	Messaging.markRead = function (uid, roomId, callback) { | 		return await db.sortedSetsAdd(keys, Date.now(), roomId); | ||||||
| 		db.sortedSetRemove('uid:' + uid + ':chat:rooms:unread', roomId, callback); |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	Messaging.markAllRead = function (uid, callback) { |  | ||||||
| 		db.delete('uid:' + uid + ':chat:rooms:unread', callback); |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	Messaging.markUnread = function (uids, roomId, callback) { |  | ||||||
| 		async.waterfall([ |  | ||||||
| 			function (next) { |  | ||||||
| 				Messaging.roomExists(roomId, next); |  | ||||||
| 			}, |  | ||||||
| 			function (exists, next) { |  | ||||||
| 				if (!exists) { |  | ||||||
| 					return next(new Error('[[error:chat-room-does-not-exist]]')); |  | ||||||
| 				} |  | ||||||
| 				var keys = uids.map(function (uid) { |  | ||||||
| 					return 'uid:' + uid + ':chat:rooms:unread'; |  | ||||||
| 				}); |  | ||||||
|  |  | ||||||
| 				db.sortedSetsAdd(keys, Date.now(), roomId, next); |  | ||||||
| 			}, |  | ||||||
| 		], callback); |  | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -278,7 +278,7 @@ SocketModules.chats.delete = function (socket, data, callback) { | |||||||
| 			Messaging.canDelete(data.messageId, socket.uid, next); | 			Messaging.canDelete(data.messageId, socket.uid, next); | ||||||
| 		}, | 		}, | ||||||
| 		function (next) { | 		function (next) { | ||||||
| 			Messaging.deleteMessage(data.messageId, data.roomId, next); | 			Messaging.deleteMessage(data.messageId, next); | ||||||
| 		}, | 		}, | ||||||
| 	], callback); | 	], callback); | ||||||
| }; | }; | ||||||
| @@ -293,7 +293,7 @@ SocketModules.chats.restore = function (socket, data, callback) { | |||||||
| 			Messaging.canDelete(data.messageId, socket.uid, next); | 			Messaging.canDelete(data.messageId, socket.uid, next); | ||||||
| 		}, | 		}, | ||||||
| 		function (next) { | 		function (next) { | ||||||
| 			Messaging.restoreMessage(data.messageId, data.roomId, next); | 			Messaging.restoreMessage(data.messageId, next); | ||||||
| 		}, | 		}, | ||||||
| 	], callback); | 	], callback); | ||||||
| }; | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user