|
|
|
|
@ -1,26 +1,23 @@ |
|
|
|
|
import { escapeRegExp } from '@rocket.chat/string-helpers'; |
|
|
|
|
import { Meteor } from 'meteor/meteor'; |
|
|
|
|
import { Match, check } from 'meteor/check'; |
|
|
|
|
import { Messages as MessagesRaw } from '@rocket.chat/models'; |
|
|
|
|
import { Messages, Users, Rooms, Subscriptions } from '@rocket.chat/models'; |
|
|
|
|
import { escapeRegExp } from '@rocket.chat/string-helpers'; |
|
|
|
|
import type { IMessage } from '@rocket.chat/core-typings'; |
|
|
|
|
|
|
|
|
|
import { Messages } from '../../../models/server'; |
|
|
|
|
import { canAccessRoom, canAccessRoomId, roomAccessAttributes, hasPermission } from '../../../authorization/server'; |
|
|
|
|
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; |
|
|
|
|
import { processWebhookMessage } from '../../../lib/server'; |
|
|
|
|
import { executeSendMessage } from '../../../lib/server/methods/sendMessage'; |
|
|
|
|
import { executeSetReaction } from '../../../reactions/server/setReaction'; |
|
|
|
|
import { API } from '../api'; |
|
|
|
|
import Rooms from '../../../models/server/models/Rooms'; |
|
|
|
|
import Users from '../../../models/server/models/Users'; |
|
|
|
|
import Subscriptions from '../../../models/server/models/Subscriptions'; |
|
|
|
|
import { processWebhookMessage } from '../../../lib/server'; |
|
|
|
|
import { settings } from '../../../settings/server'; |
|
|
|
|
import { findMentionedMessages, findStarredMessages, findDiscussionsFromRoom } from '../lib/messages'; |
|
|
|
|
import { executeSetReaction } from '../../../reactions/server/setReaction'; |
|
|
|
|
import { findDiscussionsFromRoom, findMentionedMessages, findStarredMessages } from '../lib/messages'; |
|
|
|
|
import { executeSendMessage } from '../../../lib/server/methods/sendMessage'; |
|
|
|
|
|
|
|
|
|
API.v1.addRoute( |
|
|
|
|
'chat.delete', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
async post() { |
|
|
|
|
check( |
|
|
|
|
this.bodyParams, |
|
|
|
|
Match.ObjectIncluding({ |
|
|
|
|
@ -30,7 +27,7 @@ API.v1.addRoute( |
|
|
|
|
}), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const msg = Messages.findOneById(this.bodyParams.msgId, { fields: { u: 1, rid: 1 } }); |
|
|
|
|
const msg = await Messages.findOneById(this.bodyParams.msgId, { projection: { u: 1, rid: 1 } }); |
|
|
|
|
|
|
|
|
|
if (!msg) { |
|
|
|
|
return API.v1.failure(`No message found with the id of "${this.bodyParams.msgId}".`); |
|
|
|
|
@ -44,13 +41,13 @@ API.v1.addRoute( |
|
|
|
|
return API.v1.failure('Unauthorized. You must have the permission "force-delete-message" to delete other\'s message as them.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Meteor.runAsUser(this.bodyParams.asUser ? msg.u._id : this.userId, () => { |
|
|
|
|
await Meteor.runAsUser(this.bodyParams.asUser ? msg.u._id : this.userId, () => { |
|
|
|
|
Meteor.call('deleteMessage', { _id: msg._id }); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return API.v1.success({ |
|
|
|
|
_id: msg._id, |
|
|
|
|
ts: Date.now(), |
|
|
|
|
ts: Date.now().toString(), |
|
|
|
|
message: msg, |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
@ -61,7 +58,7 @@ API.v1.addRoute( |
|
|
|
|
'chat.syncMessages', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
get() { |
|
|
|
|
async get() { |
|
|
|
|
const { roomId, lastUpdate } = this.queryParams; |
|
|
|
|
|
|
|
|
|
if (!roomId) { |
|
|
|
|
@ -74,10 +71,7 @@ API.v1.addRoute( |
|
|
|
|
throw new Meteor.Error('error-roomId-param-invalid', 'The "lastUpdate" query parameter must be a valid date.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let result; |
|
|
|
|
Meteor.runAsUser(this.userId, () => { |
|
|
|
|
result = Meteor.call('messages/get', roomId, { lastUpdate: new Date(lastUpdate) }); |
|
|
|
|
}); |
|
|
|
|
const result = await Meteor.call('messages/get', roomId, { lastUpdate: new Date(lastUpdate) }); |
|
|
|
|
|
|
|
|
|
if (!result) { |
|
|
|
|
return API.v1.failure(); |
|
|
|
|
@ -85,7 +79,7 @@ API.v1.addRoute( |
|
|
|
|
|
|
|
|
|
return API.v1.success({ |
|
|
|
|
result: { |
|
|
|
|
updated: normalizeMessagesForUser(result.updated, this.userId), |
|
|
|
|
updated: await normalizeMessagesForUser(result.updated, this.userId), |
|
|
|
|
deleted: result.deleted, |
|
|
|
|
}, |
|
|
|
|
}); |
|
|
|
|
@ -95,23 +89,22 @@ API.v1.addRoute( |
|
|
|
|
|
|
|
|
|
API.v1.addRoute( |
|
|
|
|
'chat.getMessage', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
get() { |
|
|
|
|
authRequired: true, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
async get() { |
|
|
|
|
if (!this.queryParams.msgId) { |
|
|
|
|
return API.v1.failure('The "msgId" query parameter must be provided.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let msg; |
|
|
|
|
Meteor.runAsUser(this.userId, () => { |
|
|
|
|
msg = Meteor.call('getSingleMessage', this.queryParams.msgId); |
|
|
|
|
}); |
|
|
|
|
const msg = await Meteor.call('getSingleMessage', this.queryParams.msgId); |
|
|
|
|
|
|
|
|
|
if (!msg) { |
|
|
|
|
return API.v1.failure(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const [message] = normalizeMessagesForUser([msg], this.userId); |
|
|
|
|
const [message] = await normalizeMessagesForUser([msg], this.userId); |
|
|
|
|
|
|
|
|
|
return API.v1.success({ |
|
|
|
|
message, |
|
|
|
|
@ -124,23 +117,20 @@ API.v1.addRoute( |
|
|
|
|
'chat.pinMessage', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
if (!this.bodyParams.messageId || !this.bodyParams.messageId.trim()) { |
|
|
|
|
async post() { |
|
|
|
|
if (!this.bodyParams.messageId?.trim()) { |
|
|
|
|
throw new Meteor.Error('error-messageid-param-not-provided', 'The required "messageId" param is missing.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const msg = Messages.findOneById(this.bodyParams.messageId); |
|
|
|
|
const msg = await Messages.findOneById(this.bodyParams.messageId); |
|
|
|
|
|
|
|
|
|
if (!msg) { |
|
|
|
|
throw new Meteor.Error('error-message-not-found', 'The provided "messageId" does not match any existing message.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let pinnedMessage; |
|
|
|
|
Meteor.runAsUser(this.userId, () => { |
|
|
|
|
pinnedMessage = Meteor.call('pinMessage', msg); |
|
|
|
|
}); |
|
|
|
|
const pinnedMessage = await Meteor.call('pinMessage', msg); |
|
|
|
|
|
|
|
|
|
const [message] = normalizeMessagesForUser([pinnedMessage], this.userId); |
|
|
|
|
const [message] = await normalizeMessagesForUser([pinnedMessage], this.userId); |
|
|
|
|
|
|
|
|
|
return API.v1.success({ |
|
|
|
|
message, |
|
|
|
|
@ -153,14 +143,14 @@ API.v1.addRoute( |
|
|
|
|
'chat.postMessage', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
const messageReturn = processWebhookMessage(this.bodyParams, this.user)[0]; |
|
|
|
|
async post() { |
|
|
|
|
const messageReturn = await processWebhookMessage(this.bodyParams, this.user)[0]; |
|
|
|
|
|
|
|
|
|
if (!messageReturn) { |
|
|
|
|
return API.v1.failure('unknown-error'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const [message] = normalizeMessagesForUser([messageReturn.message], this.userId); |
|
|
|
|
const [message] = await normalizeMessagesForUser([messageReturn.message], this.userId); |
|
|
|
|
|
|
|
|
|
return API.v1.success({ |
|
|
|
|
ts: Date.now(), |
|
|
|
|
@ -175,7 +165,7 @@ API.v1.addRoute( |
|
|
|
|
'chat.search', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
get() { |
|
|
|
|
async get() { |
|
|
|
|
const { roomId, searchText } = this.queryParams; |
|
|
|
|
const { offset, count } = this.getPaginationItems(); |
|
|
|
|
|
|
|
|
|
@ -187,13 +177,10 @@ API.v1.addRoute( |
|
|
|
|
throw new Meteor.Error('error-searchText-param-not-provided', 'The required "searchText" query param is missing.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let result; |
|
|
|
|
Meteor.runAsUser(this.userId, () => { |
|
|
|
|
result = Meteor.call('messageSearch', searchText, roomId, count, offset).message.docs; |
|
|
|
|
}); |
|
|
|
|
const result = await Meteor.call('messageSearch', searchText, roomId, count, offset).message.docs; |
|
|
|
|
|
|
|
|
|
return API.v1.success({ |
|
|
|
|
messages: normalizeMessagesForUser(result, this.userId), |
|
|
|
|
messages: await normalizeMessagesForUser(result, this.userId), |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
@ -206,13 +193,13 @@ API.v1.addRoute( |
|
|
|
|
'chat.sendMessage', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
async post() { |
|
|
|
|
if (!this.bodyParams.message) { |
|
|
|
|
throw new Meteor.Error('error-invalid-params', 'The "message" parameter must be provided.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const sent = executeSendMessage(this.userId, this.bodyParams.message); |
|
|
|
|
const [message] = normalizeMessagesForUser([sent], this.userId); |
|
|
|
|
const sent = await executeSendMessage(this.userId, this.bodyParams.message as Pick<IMessage, 'rid'>); |
|
|
|
|
const [message] = await normalizeMessagesForUser([sent], this.userId); |
|
|
|
|
|
|
|
|
|
return API.v1.success({ |
|
|
|
|
message, |
|
|
|
|
@ -225,24 +212,22 @@ API.v1.addRoute( |
|
|
|
|
'chat.starMessage', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
if (!this.bodyParams.messageId || !this.bodyParams.messageId.trim()) { |
|
|
|
|
async post() { |
|
|
|
|
if (!this.bodyParams.messageId?.trim()) { |
|
|
|
|
throw new Meteor.Error('error-messageid-param-not-provided', 'The required "messageId" param is required.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const msg = Messages.findOneById(this.bodyParams.messageId); |
|
|
|
|
const msg = await Messages.findOneById(this.bodyParams.messageId); |
|
|
|
|
|
|
|
|
|
if (!msg) { |
|
|
|
|
throw new Meteor.Error('error-message-not-found', 'The provided "messageId" does not match any existing message.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Meteor.runAsUser(this.userId, () => |
|
|
|
|
Meteor.call('starMessage', { |
|
|
|
|
_id: msg._id, |
|
|
|
|
rid: msg.rid, |
|
|
|
|
starred: true, |
|
|
|
|
}), |
|
|
|
|
); |
|
|
|
|
await Meteor.call('starMessage', { |
|
|
|
|
_id: msg._id, |
|
|
|
|
rid: msg.rid, |
|
|
|
|
starred: true, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return API.v1.success(); |
|
|
|
|
}, |
|
|
|
|
@ -253,18 +238,18 @@ API.v1.addRoute( |
|
|
|
|
'chat.unPinMessage', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
if (!this.bodyParams.messageId || !this.bodyParams.messageId.trim()) { |
|
|
|
|
async post() { |
|
|
|
|
if (!this.bodyParams.messageId?.trim()) { |
|
|
|
|
throw new Meteor.Error('error-messageid-param-not-provided', 'The required "messageId" param is required.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const msg = Messages.findOneById(this.bodyParams.messageId); |
|
|
|
|
const msg = await Messages.findOneById(this.bodyParams.messageId); |
|
|
|
|
|
|
|
|
|
if (!msg) { |
|
|
|
|
throw new Meteor.Error('error-message-not-found', 'The provided "messageId" does not match any existing message.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Meteor.runAsUser(this.userId, () => Meteor.call('unpinMessage', msg)); |
|
|
|
|
await Meteor.call('unpinMessage', msg); |
|
|
|
|
|
|
|
|
|
return API.v1.success(); |
|
|
|
|
}, |
|
|
|
|
@ -275,24 +260,22 @@ API.v1.addRoute( |
|
|
|
|
'chat.unStarMessage', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
if (!this.bodyParams.messageId || !this.bodyParams.messageId.trim()) { |
|
|
|
|
async post() { |
|
|
|
|
if (!this.bodyParams.messageId?.trim()) { |
|
|
|
|
throw new Meteor.Error('error-messageid-param-not-provided', 'The required "messageId" param is required.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const msg = Messages.findOneById(this.bodyParams.messageId); |
|
|
|
|
const msg = await Messages.findOneById(this.bodyParams.messageId); |
|
|
|
|
|
|
|
|
|
if (!msg) { |
|
|
|
|
throw new Meteor.Error('error-message-not-found', 'The provided "messageId" does not match any existing message.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Meteor.runAsUser(this.userId, () => |
|
|
|
|
Meteor.call('starMessage', { |
|
|
|
|
_id: msg._id, |
|
|
|
|
rid: msg.rid, |
|
|
|
|
starred: false, |
|
|
|
|
}), |
|
|
|
|
); |
|
|
|
|
await Meteor.call('starMessage', { |
|
|
|
|
_id: msg._id, |
|
|
|
|
rid: msg.rid, |
|
|
|
|
starred: false, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return API.v1.success(); |
|
|
|
|
}, |
|
|
|
|
@ -303,7 +286,7 @@ API.v1.addRoute( |
|
|
|
|
'chat.update', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
async post() { |
|
|
|
|
check( |
|
|
|
|
this.bodyParams, |
|
|
|
|
Match.ObjectIncluding({ |
|
|
|
|
@ -313,7 +296,7 @@ API.v1.addRoute( |
|
|
|
|
}), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const msg = Messages.findOneById(this.bodyParams.msgId); |
|
|
|
|
const msg = await Messages.findOneById(this.bodyParams.msgId); |
|
|
|
|
|
|
|
|
|
// Ensure the message exists
|
|
|
|
|
if (!msg) { |
|
|
|
|
@ -325,11 +308,10 @@ API.v1.addRoute( |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Permission checks are already done in the updateMessage method, so no need to duplicate them
|
|
|
|
|
Meteor.runAsUser(this.userId, () => { |
|
|
|
|
Meteor.call('updateMessage', { _id: msg._id, msg: this.bodyParams.text, rid: msg.rid }); |
|
|
|
|
}); |
|
|
|
|
await Meteor.call('updateMessage', { _id: msg._id, msg: this.bodyParams.text, rid: msg.rid }); |
|
|
|
|
|
|
|
|
|
const [message] = normalizeMessagesForUser([Messages.findOneById(msg._id)], this.userId); |
|
|
|
|
const updatedMessage = await Messages.findOneById(msg._id); |
|
|
|
|
const [message] = await normalizeMessagesForUser(updatedMessage ? [updatedMessage] : [], this.userId); |
|
|
|
|
|
|
|
|
|
return API.v1.success({ |
|
|
|
|
message, |
|
|
|
|
@ -342,24 +324,24 @@ API.v1.addRoute( |
|
|
|
|
'chat.react', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
if (!this.bodyParams.messageId || !this.bodyParams.messageId.trim()) { |
|
|
|
|
async post() { |
|
|
|
|
if (!this.bodyParams.messageId?.trim()) { |
|
|
|
|
throw new Meteor.Error('error-messageid-param-not-provided', 'The required "messageId" param is missing.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const msg = Messages.findOneById(this.bodyParams.messageId); |
|
|
|
|
const msg = await Messages.findOneById(this.bodyParams.messageId); |
|
|
|
|
|
|
|
|
|
if (!msg) { |
|
|
|
|
throw new Meteor.Error('error-message-not-found', 'The provided "messageId" does not match any existing message.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const emoji = this.bodyParams.emoji || this.bodyParams.reaction; |
|
|
|
|
const emoji = 'emoji' in this.bodyParams ? this.bodyParams.emoji : (this.bodyParams as { reaction: string }).reaction; |
|
|
|
|
|
|
|
|
|
if (!emoji) { |
|
|
|
|
throw new Meteor.Error('error-emoji-param-not-provided', 'The required "emoji" param is missing.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Meteor.runAsUser(this.userId, () => Promise.await(executeSetReaction(emoji, msg._id, this.bodyParams.shouldReact))); |
|
|
|
|
await executeSetReaction(emoji, msg._id, this.bodyParams.shouldReact); |
|
|
|
|
|
|
|
|
|
return API.v1.success(); |
|
|
|
|
}, |
|
|
|
|
@ -370,7 +352,7 @@ API.v1.addRoute( |
|
|
|
|
'chat.reportMessage', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
async post() { |
|
|
|
|
const { messageId, description } = this.bodyParams; |
|
|
|
|
if (!messageId) { |
|
|
|
|
return API.v1.failure('The required "messageId" param is missing.'); |
|
|
|
|
@ -380,7 +362,7 @@ API.v1.addRoute( |
|
|
|
|
return API.v1.failure('The required "description" param is missing.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Meteor.call('reportMessage', messageId, description); |
|
|
|
|
await Meteor.call('reportMessage', messageId, description); |
|
|
|
|
|
|
|
|
|
return API.v1.success(); |
|
|
|
|
}, |
|
|
|
|
@ -391,21 +373,21 @@ API.v1.addRoute( |
|
|
|
|
'chat.ignoreUser', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
get() { |
|
|
|
|
async get() { |
|
|
|
|
const { rid, userId } = this.queryParams; |
|
|
|
|
let { ignore = true } = this.queryParams; |
|
|
|
|
|
|
|
|
|
ignore = typeof ignore === 'string' ? /true|1/.test(ignore) : ignore; |
|
|
|
|
|
|
|
|
|
if (!rid || !rid.trim()) { |
|
|
|
|
if (!rid?.trim()) { |
|
|
|
|
throw new Meteor.Error('error-room-id-param-not-provided', 'The required "rid" param is missing.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!userId || !userId.trim()) { |
|
|
|
|
if (!userId?.trim()) { |
|
|
|
|
throw new Meteor.Error('error-user-id-param-not-provided', 'The required "userId" param is missing.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Meteor.runAsUser(this.userId, () => Meteor.call('ignoreUser', { rid, userId, ignore })); |
|
|
|
|
await Meteor.call('ignoreUser', { rid, userId, ignore }); |
|
|
|
|
|
|
|
|
|
return API.v1.success(); |
|
|
|
|
}, |
|
|
|
|
@ -430,7 +412,7 @@ API.v1.addRoute( |
|
|
|
|
throw new Meteor.Error('The "since" query parameter must be a valid date.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const { cursor, totalCount } = MessagesRaw.trashFindPaginatedDeletedAfter( |
|
|
|
|
const { cursor, totalCount } = await Messages.trashFindPaginatedDeletedAfter( |
|
|
|
|
new Date(since), |
|
|
|
|
{ rid: roomId }, |
|
|
|
|
{ |
|
|
|
|
@ -468,7 +450,7 @@ API.v1.addRoute( |
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Not allowed'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const { cursor, totalCount } = MessagesRaw.findPaginatedPinnedByRoom(roomId, { |
|
|
|
|
const { cursor, totalCount } = await Messages.findPaginatedPinnedByRoom(roomId, { |
|
|
|
|
skip: offset, |
|
|
|
|
limit: count, |
|
|
|
|
}); |
|
|
|
|
@ -476,7 +458,7 @@ API.v1.addRoute( |
|
|
|
|
const [messages, total] = await Promise.all([cursor.toArray(), totalCount]); |
|
|
|
|
|
|
|
|
|
return API.v1.success({ |
|
|
|
|
messages: normalizeMessagesForUser(messages, this.userId), |
|
|
|
|
messages: await normalizeMessagesForUser(messages, this.userId), |
|
|
|
|
count: messages.length, |
|
|
|
|
offset, |
|
|
|
|
total, |
|
|
|
|
@ -498,25 +480,25 @@ API.v1.addRoute( |
|
|
|
|
const { offset, count } = this.getPaginationItems(); |
|
|
|
|
const { sort, fields, query } = this.parseJsonQuery(); |
|
|
|
|
|
|
|
|
|
if (!settings.get('Threads_enabled')) { |
|
|
|
|
if (!settings.get<boolean>('Threads_enabled')) { |
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Threads Disabled'); |
|
|
|
|
} |
|
|
|
|
const user = Users.findOneById(this.userId, { fields: { _id: 1 } }); |
|
|
|
|
const room = Rooms.findOneById(rid, { fields: { ...roomAccessAttributes, t: 1, _id: 1 } }); |
|
|
|
|
const user = await Users.findOneById(this.userId, { projection: { _id: 1 } }); |
|
|
|
|
const room = await Rooms.findOneById(rid, { projection: { ...roomAccessAttributes, t: 1, _id: 1 } }); |
|
|
|
|
|
|
|
|
|
if (!canAccessRoom(room, user)) { |
|
|
|
|
if (!room || !user || !canAccessRoom(room, user)) { |
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Not Allowed'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const typeThread = { |
|
|
|
|
_hidden: { $ne: true }, |
|
|
|
|
...(type === 'following' && { replies: { $in: [this.userId] } }), |
|
|
|
|
...(type === 'unread' && { _id: { $in: Subscriptions.findOneByRoomIdAndUserId(room._id, user._id).tunread } }), |
|
|
|
|
msg: new RegExp(escapeRegExp(text), 'i'), |
|
|
|
|
...(type === 'unread' && { _id: { $in: (await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id))?.tunread || [] } }), |
|
|
|
|
msg: new RegExp(escapeRegExp(text || ''), 'i'), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const threadQuery = { ...query, ...typeThread, rid: room._id, tcount: { $exists: true } }; |
|
|
|
|
const { cursor, totalCount } = MessagesRaw.findPaginated(threadQuery, { |
|
|
|
|
const { cursor, totalCount } = await Messages.findPaginated(threadQuery, { |
|
|
|
|
sort: sort || { tlm: -1 }, |
|
|
|
|
skip: offset, |
|
|
|
|
limit: count, |
|
|
|
|
@ -526,7 +508,7 @@ API.v1.addRoute( |
|
|
|
|
const [threads, total] = await Promise.all([cursor.toArray(), totalCount]); |
|
|
|
|
|
|
|
|
|
return API.v1.success({ |
|
|
|
|
threads: normalizeMessagesForUser(threads, this.userId), |
|
|
|
|
threads: await normalizeMessagesForUser(threads, this.userId), |
|
|
|
|
count: threads.length, |
|
|
|
|
offset, |
|
|
|
|
total, |
|
|
|
|
@ -539,12 +521,12 @@ API.v1.addRoute( |
|
|
|
|
'chat.syncThreadsList', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
get() { |
|
|
|
|
async get() { |
|
|
|
|
const { rid } = this.queryParams; |
|
|
|
|
const { query, fields, sort } = this.parseJsonQuery(); |
|
|
|
|
const { updatedSince } = this.queryParams; |
|
|
|
|
let updatedSinceDate; |
|
|
|
|
if (!settings.get('Threads_enabled')) { |
|
|
|
|
if (!settings.get<boolean>('Threads_enabled')) { |
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Threads Disabled'); |
|
|
|
|
} |
|
|
|
|
if (!rid) { |
|
|
|
|
@ -558,17 +540,26 @@ API.v1.addRoute( |
|
|
|
|
} else { |
|
|
|
|
updatedSinceDate = new Date(updatedSince); |
|
|
|
|
} |
|
|
|
|
const user = Users.findOneById(this.userId, { fields: { _id: 1 } }); |
|
|
|
|
const room = Rooms.findOneById(rid, { fields: { ...roomAccessAttributes, t: 1, _id: 1 } }); |
|
|
|
|
const user = await Users.findOneById(this.userId, { projection: { _id: 1 } }); |
|
|
|
|
const room = await Rooms.findOneById(rid, { projection: { ...roomAccessAttributes, t: 1, _id: 1 } }); |
|
|
|
|
|
|
|
|
|
if (!canAccessRoom(room, user)) { |
|
|
|
|
if (!room || !user || !canAccessRoom(room, user)) { |
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Not Allowed'); |
|
|
|
|
} |
|
|
|
|
const threadQuery = Object.assign({}, query, { rid, tcount: { $exists: true } }); |
|
|
|
|
return API.v1.success({ |
|
|
|
|
threads: { |
|
|
|
|
update: Messages.find({ ...threadQuery, _updatedAt: { $gt: updatedSinceDate } }, { fields, sort }).fetch(), |
|
|
|
|
remove: Messages.trashFindDeletedAfter(updatedSinceDate, threadQuery, { fields, sort }).fetch(), |
|
|
|
|
update: await Messages.find( |
|
|
|
|
{ ...threadQuery, _updatedAt: { $gt: updatedSinceDate } }, |
|
|
|
|
{ |
|
|
|
|
sort, |
|
|
|
|
projection: fields, |
|
|
|
|
}, |
|
|
|
|
).toArray(), |
|
|
|
|
remove: await Messages.trashFindDeletedAfter(updatedSinceDate, threadQuery, { |
|
|
|
|
sort, |
|
|
|
|
projection: fields, |
|
|
|
|
}).toArray(), |
|
|
|
|
}, |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
@ -590,17 +581,17 @@ API.v1.addRoute( |
|
|
|
|
if (!tmid) { |
|
|
|
|
throw new Meteor.Error('error-invalid-params', 'The required "tmid" query param is missing.'); |
|
|
|
|
} |
|
|
|
|
const thread = Messages.findOneById(tmid, { fields: { rid: 1 } }); |
|
|
|
|
if (!thread || !thread.rid) { |
|
|
|
|
const thread = await Messages.findOneById(tmid, { projection: { rid: 1 } }); |
|
|
|
|
if (!thread?.rid) { |
|
|
|
|
throw new Meteor.Error('error-invalid-message', 'Invalid Message'); |
|
|
|
|
} |
|
|
|
|
const user = Users.findOneById(this.userId, { fields: { _id: 1 } }); |
|
|
|
|
const room = Rooms.findOneById(thread.rid, { fields: { ...roomAccessAttributes, t: 1, _id: 1 } }); |
|
|
|
|
const user = await Users.findOneById(this.userId, { projection: { _id: 1 } }); |
|
|
|
|
const room = await Rooms.findOneById(thread.rid, { projection: { ...roomAccessAttributes, t: 1, _id: 1 } }); |
|
|
|
|
|
|
|
|
|
if (!canAccessRoom(room, user)) { |
|
|
|
|
if (!room || !user || !canAccessRoom(room, user)) { |
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Not Allowed'); |
|
|
|
|
} |
|
|
|
|
const { cursor, totalCount } = MessagesRaw.findPaginated( |
|
|
|
|
const { cursor, totalCount } = await Messages.findPaginated( |
|
|
|
|
{ ...query, tmid }, |
|
|
|
|
{ |
|
|
|
|
sort: sort || { ts: 1 }, |
|
|
|
|
@ -626,12 +617,12 @@ API.v1.addRoute( |
|
|
|
|
'chat.syncThreadMessages', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
get() { |
|
|
|
|
async get() { |
|
|
|
|
const { tmid } = this.queryParams; |
|
|
|
|
const { query, fields, sort } = this.parseJsonQuery(); |
|
|
|
|
const { updatedSince } = this.queryParams; |
|
|
|
|
let updatedSinceDate; |
|
|
|
|
if (!settings.get('Threads_enabled')) { |
|
|
|
|
if (!settings.get<boolean>('Threads_enabled')) { |
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Threads Disabled'); |
|
|
|
|
} |
|
|
|
|
if (!tmid) { |
|
|
|
|
@ -645,20 +636,20 @@ API.v1.addRoute( |
|
|
|
|
} else { |
|
|
|
|
updatedSinceDate = new Date(updatedSince); |
|
|
|
|
} |
|
|
|
|
const thread = Messages.findOneById(tmid, { fields: { rid: 1 } }); |
|
|
|
|
if (!thread || !thread.rid) { |
|
|
|
|
const thread = await Messages.findOneById(tmid, { projection: { rid: 1 } }); |
|
|
|
|
if (!thread?.rid) { |
|
|
|
|
throw new Meteor.Error('error-invalid-message', 'Invalid Message'); |
|
|
|
|
} |
|
|
|
|
const user = Users.findOneById(this.userId, { fields: { _id: 1 } }); |
|
|
|
|
const room = Rooms.findOneById(thread.rid, { fields: { ...roomAccessAttributes, t: 1, _id: 1 } }); |
|
|
|
|
const user = await Users.findOneById(this.userId, { projection: { _id: 1 } }); |
|
|
|
|
const room = await Rooms.findOneById(thread.rid, { projection: { ...roomAccessAttributes, t: 1, _id: 1 } }); |
|
|
|
|
|
|
|
|
|
if (!canAccessRoom(room, user)) { |
|
|
|
|
if (!room || !user || !canAccessRoom(room, user)) { |
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Not Allowed'); |
|
|
|
|
} |
|
|
|
|
return API.v1.success({ |
|
|
|
|
messages: { |
|
|
|
|
update: Messages.find({ ...query, tmid, _updatedAt: { $gt: updatedSinceDate } }, { fields, sort }).fetch(), |
|
|
|
|
remove: Messages.trashFindDeletedAfter(updatedSinceDate, { ...query, tmid }, { fields, sort }).fetch(), |
|
|
|
|
update: await Messages.find({ ...query, tmid, _updatedAt: { $gt: updatedSinceDate } }, { projection: fields, sort }).toArray(), |
|
|
|
|
remove: await Messages.trashFindDeletedAfter(updatedSinceDate, { ...query, tmid }, { projection: fields, sort }).toArray(), |
|
|
|
|
}, |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
@ -669,13 +660,15 @@ API.v1.addRoute( |
|
|
|
|
'chat.followMessage', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
async post() { |
|
|
|
|
const { mid } = this.bodyParams; |
|
|
|
|
|
|
|
|
|
if (!mid) { |
|
|
|
|
throw new Meteor.Error('The required "mid" body param is missing.'); |
|
|
|
|
} |
|
|
|
|
Meteor.runAsUser(this.userId, () => Meteor.call('followMessage', { mid })); |
|
|
|
|
|
|
|
|
|
await Meteor.call('followMessage', { mid }); |
|
|
|
|
|
|
|
|
|
return API.v1.success(); |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
@ -685,13 +678,15 @@ API.v1.addRoute( |
|
|
|
|
'chat.unfollowMessage', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
post() { |
|
|
|
|
async post() { |
|
|
|
|
const { mid } = this.bodyParams; |
|
|
|
|
|
|
|
|
|
if (!mid) { |
|
|
|
|
throw new Meteor.Error('The required "mid" body param is missing.'); |
|
|
|
|
} |
|
|
|
|
Meteor.runAsUser(this.userId, () => Meteor.call('unfollowMessage', { mid })); |
|
|
|
|
|
|
|
|
|
await Meteor.call('unfollowMessage', { mid }); |
|
|
|
|
|
|
|
|
|
return API.v1.success(); |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
@ -701,24 +696,23 @@ API.v1.addRoute( |
|
|
|
|
'chat.getMentionedMessages', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
get() { |
|
|
|
|
async get() { |
|
|
|
|
const { roomId } = this.queryParams; |
|
|
|
|
const { sort } = this.parseJsonQuery(); |
|
|
|
|
const { offset, count } = this.getPaginationItems(); |
|
|
|
|
if (!roomId) { |
|
|
|
|
throw new Meteor.Error('error-invalid-params', 'The required "roomId" query param is missing.'); |
|
|
|
|
} |
|
|
|
|
const messages = Promise.await( |
|
|
|
|
findMentionedMessages({ |
|
|
|
|
uid: this.userId, |
|
|
|
|
roomId, |
|
|
|
|
pagination: { |
|
|
|
|
offset, |
|
|
|
|
count, |
|
|
|
|
sort, |
|
|
|
|
}, |
|
|
|
|
}), |
|
|
|
|
); |
|
|
|
|
const messages = await findMentionedMessages({ |
|
|
|
|
uid: this.userId, |
|
|
|
|
roomId, |
|
|
|
|
pagination: { |
|
|
|
|
offset, |
|
|
|
|
count, |
|
|
|
|
sort, |
|
|
|
|
}, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return API.v1.success(messages); |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
@ -728,7 +722,7 @@ API.v1.addRoute( |
|
|
|
|
'chat.getStarredMessages', |
|
|
|
|
{ authRequired: true }, |
|
|
|
|
{ |
|
|
|
|
get() { |
|
|
|
|
async get() { |
|
|
|
|
const { roomId } = this.queryParams; |
|
|
|
|
const { sort } = this.parseJsonQuery(); |
|
|
|
|
const { offset, count } = this.getPaginationItems(); |
|
|
|
|
@ -736,19 +730,17 @@ API.v1.addRoute( |
|
|
|
|
if (!roomId) { |
|
|
|
|
throw new Meteor.Error('error-invalid-params', 'The required "roomId" query param is missing.'); |
|
|
|
|
} |
|
|
|
|
const messages = Promise.await( |
|
|
|
|
findStarredMessages({ |
|
|
|
|
uid: this.userId, |
|
|
|
|
roomId, |
|
|
|
|
pagination: { |
|
|
|
|
offset, |
|
|
|
|
count, |
|
|
|
|
sort, |
|
|
|
|
}, |
|
|
|
|
}), |
|
|
|
|
); |
|
|
|
|
const messages = await findStarredMessages({ |
|
|
|
|
uid: this.userId, |
|
|
|
|
roomId, |
|
|
|
|
pagination: { |
|
|
|
|
offset, |
|
|
|
|
count, |
|
|
|
|
sort, |
|
|
|
|
}, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
messages.messages = normalizeMessagesForUser(messages.messages, this.userId); |
|
|
|
|
messages.messages = await normalizeMessagesForUser(messages.messages, this.userId); |
|
|
|
|
|
|
|
|
|
return API.v1.success(messages); |
|
|
|
|
}, |
|
|
|
|
@ -770,7 +762,7 @@ API.v1.addRoute( |
|
|
|
|
const messages = await findDiscussionsFromRoom({ |
|
|
|
|
uid: this.userId, |
|
|
|
|
roomId, |
|
|
|
|
text, |
|
|
|
|
text: text || '', |
|
|
|
|
pagination: { |
|
|
|
|
offset, |
|
|
|
|
count, |