|
|
@ -8,10 +8,13 @@ import mongoose from 'mongoose'; |
|
|
|
const ChatRoom = mongoose.model('ChatRoom'); |
|
|
|
const ChatMessage = mongoose.model('ChatMessage'); |
|
|
|
const ChatRoomInvite = mongoose.model('ChatRoomInvite'); |
|
|
|
const User = mongoose.model('User'); |
|
|
|
|
|
|
|
import numeral from 'numeral'; |
|
|
|
import dayjs from 'dayjs'; |
|
|
|
|
|
|
|
import { v4 as uuidv4 } from 'uuid'; |
|
|
|
|
|
|
|
import { SiteService, SiteError } from '../../lib/site-lib.js'; |
|
|
|
import { MAX_ROOM_CAPACITY } from '../models/lib/constants.js'; |
|
|
|
|
|
|
@ -41,8 +44,10 @@ export default class ChatService extends SiteService { |
|
|
|
{ |
|
|
|
path: 'present', |
|
|
|
select: userService.USER_SELECT, |
|
|
|
populate: userService.populateUser, |
|
|
|
}, |
|
|
|
]; |
|
|
|
|
|
|
|
this.populateChatMessage = [ |
|
|
|
{ |
|
|
|
path: 'channel', |
|
|
@ -75,6 +80,21 @@ export default class ChatService extends SiteService { |
|
|
|
], |
|
|
|
}, |
|
|
|
]; |
|
|
|
|
|
|
|
this.populateChatRoomInvite = [ |
|
|
|
{ |
|
|
|
path: 'owner', |
|
|
|
select: userService.USER_SELECT, |
|
|
|
}, |
|
|
|
{ |
|
|
|
path: 'room', |
|
|
|
populate: this.populateChatRoom, |
|
|
|
}, |
|
|
|
{ |
|
|
|
path: 'member.user', |
|
|
|
select: userService.USER_SELECT, |
|
|
|
}, |
|
|
|
]; |
|
|
|
} |
|
|
|
|
|
|
|
async createRoom (owner, roomDefinition) { |
|
|
@ -127,6 +147,97 @@ export default class ChatService extends SiteService { |
|
|
|
await ChatRoom.deleteOne({ _id: room._id }); |
|
|
|
} |
|
|
|
|
|
|
|
async inviteUserToRoom (room, inviteDefinition) { |
|
|
|
const { text: textService } = this.dtp.services; |
|
|
|
const NOW = new Date(); |
|
|
|
|
|
|
|
inviteDefinition.usernameOrEmail = inviteDefinition.usernameOrEmail.trim().toLowerCase(); |
|
|
|
|
|
|
|
const invitee = await User.findOne({ |
|
|
|
$or: [ |
|
|
|
{ username_lc: inviteDefinition.usernameOrEmail }, |
|
|
|
{ email: inviteDefinition.usernameOrEmail }, |
|
|
|
], |
|
|
|
}).lean(); |
|
|
|
if (invitee) { |
|
|
|
throw new SiteError(400, 'User already invited to room'); |
|
|
|
} |
|
|
|
|
|
|
|
const invite = new ChatRoomInvite(); |
|
|
|
invite.created = NOW; |
|
|
|
invite.token = uuidv4(); |
|
|
|
invite.owner = room.owner._id; |
|
|
|
invite.room = room._id; |
|
|
|
|
|
|
|
let userAccount = await User.findOne({ email: inviteDefinition.usernameOrEmail }); |
|
|
|
if (!userAccount) { |
|
|
|
userAccount = await User.findOne({ username_lc: inviteDefinition.usernameOrEmail.toLowerCase() }); |
|
|
|
} |
|
|
|
|
|
|
|
if (userAccount) { |
|
|
|
invite.member = { user: userAccount._id }; |
|
|
|
} else { |
|
|
|
invite.member = { email: inviteDefinition.usernameOrEmail.trim() }; |
|
|
|
} |
|
|
|
|
|
|
|
invite.status = 'new'; |
|
|
|
if (inviteDefinition.message) { |
|
|
|
invite.message = textService.filter(inviteDefinition.message); |
|
|
|
} |
|
|
|
|
|
|
|
this.log.info('inviting user to chat room', { |
|
|
|
user: inviteDefinition.usernameOrEmail, |
|
|
|
room: { |
|
|
|
_id: room._id, |
|
|
|
name: room.name, |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
await invite.save(); |
|
|
|
|
|
|
|
return invite.toObject(); |
|
|
|
} |
|
|
|
|
|
|
|
async getInvitationsForUser (user, pagination) { |
|
|
|
const invites = await ChatRoomInvite |
|
|
|
.find({ 'member.user': user._id, status: 'new' }) |
|
|
|
.skip(pagination.skip) |
|
|
|
.limit(pagination.cpp) |
|
|
|
.populate(this.populateChatRoomInvite) |
|
|
|
.lean(); |
|
|
|
return invites; |
|
|
|
} |
|
|
|
|
|
|
|
async getInviteById (inviteId) { |
|
|
|
const invite = await ChatRoomInvite |
|
|
|
.findOne({ _id: inviteId }) |
|
|
|
.populate(this.populateChatRoomInvite) |
|
|
|
.lean(); |
|
|
|
return invite; |
|
|
|
} |
|
|
|
|
|
|
|
async processRoomInvite (user, invite, action) { |
|
|
|
await ChatRoom.updateOne( |
|
|
|
{ _id: invite.room._id }, |
|
|
|
{ |
|
|
|
$addToSet: { members: user._id }, |
|
|
|
}, |
|
|
|
); |
|
|
|
|
|
|
|
await ChatRoomInvite.updateOne( |
|
|
|
{ _id: invite._id }, |
|
|
|
{ |
|
|
|
$set: { |
|
|
|
'member.user': user._id, |
|
|
|
status: action, |
|
|
|
}, |
|
|
|
$unset: { |
|
|
|
'member.email': 1, |
|
|
|
}, |
|
|
|
}, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
async joinRoom (room, user) { |
|
|
|
const roomData = await ChatRoom.findOne({ _id: room._id, banned: user._id }).lean(); |
|
|
|
if (roomData) { |
|
|
@ -176,7 +287,13 @@ export default class ChatService extends SiteService { |
|
|
|
* joined the room. |
|
|
|
*/ |
|
|
|
const displayList = this.createDisplayList('chat-control'); |
|
|
|
displayList.removeElement(`ul#present-members li[data-member-id="${member._id}"]`); |
|
|
|
displayList.removeElement( |
|
|
|
`ul#chat-active-members li[data-member-id="${member._id}"]`, |
|
|
|
); |
|
|
|
displayList.removeElement( |
|
|
|
`ul#chat-idle-members li[data-member-id="${member._id}"]`, |
|
|
|
); |
|
|
|
|
|
|
|
displayList.addElement( |
|
|
|
`ul#chat-active-members[data-room-id="${room._id}"]`, |
|
|
|
'afterBegin', |
|
|
@ -188,17 +305,17 @@ export default class ChatService extends SiteService { |
|
|
|
numeral(roomData.stats.presentCount).format('0,0'), |
|
|
|
); |
|
|
|
|
|
|
|
const systemMessage = { |
|
|
|
created: NOW.toISOString(), |
|
|
|
content: `${member.displayName || member.username} has entered the room.`, |
|
|
|
}; |
|
|
|
// const systemMessage = {
|
|
|
|
// created: NOW.toISOString(),
|
|
|
|
// content: `${member.displayName || member.username} has entered the room.`,
|
|
|
|
// };
|
|
|
|
|
|
|
|
this.dtp.emitter |
|
|
|
.to(room._id.toString()) |
|
|
|
.emit('chat-control', { |
|
|
|
displayList, |
|
|
|
audio: { playSound: 'chat-room-connect' }, |
|
|
|
systemMessages: [systemMessage], |
|
|
|
// systemMessages: [systemMessage],
|
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
@ -468,6 +585,7 @@ export default class ChatService extends SiteService { |
|
|
|
async getRoomById (roomId) { |
|
|
|
const room = await ChatRoom |
|
|
|
.findOne({ _id: roomId }) |
|
|
|
.select('+present') |
|
|
|
.populate(this.populateChatRoom) |
|
|
|
.lean(); |
|
|
|
return room; |
|
|
|