You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
968 lines
16 KiB
968 lines
16 KiB
import { Base } from './_Base';
|
|
import Messages from './Messages';
|
|
import Subscriptions from './Subscriptions';
|
|
import _ from 'underscore';
|
|
import s from 'underscore.string';
|
|
|
|
export class Rooms extends Base {
|
|
constructor(...args) {
|
|
super(...args);
|
|
|
|
this.tryEnsureIndex({ name: 1 }, { unique: 1, sparse: 1 });
|
|
this.tryEnsureIndex({ default: 1 });
|
|
this.tryEnsureIndex({ t: 1 });
|
|
this.tryEnsureIndex({ 'u._id': 1 });
|
|
}
|
|
|
|
findOneByIdOrName(_idOrName, options) {
|
|
const query = {
|
|
$or: [{
|
|
_id: _idOrName,
|
|
}, {
|
|
name: _idOrName,
|
|
}],
|
|
};
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
updateLastMessageStar(roomId, userId, starred) {
|
|
let update;
|
|
const query = { _id: roomId };
|
|
|
|
if (starred) {
|
|
update = {
|
|
$addToSet: {
|
|
'lastMessage.starred': { _id: userId },
|
|
},
|
|
};
|
|
} else {
|
|
update = {
|
|
$pull: {
|
|
'lastMessage.starred': { _id: userId },
|
|
},
|
|
};
|
|
}
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setLastMessageSnippeted(roomId, message, snippetName, snippetedBy, snippeted, snippetedAt) {
|
|
const query = { _id: roomId };
|
|
|
|
const msg = `\`\`\`${ message.msg }\`\`\``;
|
|
|
|
const update = {
|
|
$set: {
|
|
'lastMessage.msg': msg,
|
|
'lastMessage.snippeted': snippeted,
|
|
'lastMessage.snippetedAt': snippetedAt || new Date,
|
|
'lastMessage.snippetedBy': snippetedBy,
|
|
'lastMessage.snippetName': snippetName,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setLastMessagePinned(roomId, pinnedBy, pinned, pinnedAt) {
|
|
const query = { _id: roomId };
|
|
|
|
const update = {
|
|
$set: {
|
|
'lastMessage.pinned': pinned,
|
|
'lastMessage.pinnedAt': pinnedAt || new Date,
|
|
'lastMessage.pinnedBy': pinnedBy,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setSentiment(roomId, sentiment) {
|
|
return this.update({ _id: roomId }, { $set: { sentiment } });
|
|
}
|
|
|
|
setDescriptionById(_id, description) {
|
|
const query = {
|
|
_id,
|
|
};
|
|
const update = {
|
|
$set: {
|
|
description,
|
|
},
|
|
};
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setStreamingOptionsById(_id, streamingOptions) {
|
|
const update = {
|
|
$set: {
|
|
streamingOptions,
|
|
},
|
|
};
|
|
return this.update({ _id }, update);
|
|
}
|
|
|
|
setTokenpassById(_id, tokenpass) {
|
|
const update = {
|
|
$set: {
|
|
tokenpass,
|
|
},
|
|
};
|
|
|
|
return this.update({ _id }, update);
|
|
}
|
|
|
|
setReadOnlyById(_id, readOnly, hasPermission) {
|
|
if (!hasPermission) {
|
|
throw new Error('You must provide "hasPermission" function to be able to call this method');
|
|
}
|
|
const query = {
|
|
_id,
|
|
};
|
|
const update = {
|
|
$set: {
|
|
ro: readOnly,
|
|
muted: [],
|
|
},
|
|
};
|
|
if (readOnly) {
|
|
Subscriptions.findByRoomIdWhenUsernameExists(_id, { fields: { 'u._id': 1, 'u.username': 1 } }).forEach(function({ u: user }) {
|
|
if (hasPermission(user._id, 'post-readonly')) {
|
|
return;
|
|
}
|
|
return update.$set.muted.push(user.username);
|
|
});
|
|
} else {
|
|
update.$unset = {
|
|
muted: '',
|
|
};
|
|
}
|
|
|
|
if (update.$set.muted.length === 0) {
|
|
delete update.$set.muted;
|
|
}
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setAllowReactingWhenReadOnlyById = function(_id, allowReacting) {
|
|
const query = {
|
|
_id,
|
|
};
|
|
const update = {
|
|
$set: {
|
|
reactWhenReadOnly: allowReacting,
|
|
},
|
|
};
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setSystemMessagesById = function(_id, systemMessages) {
|
|
const query = {
|
|
_id,
|
|
};
|
|
const update = {
|
|
$set: {
|
|
sysMes: systemMessages,
|
|
},
|
|
};
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setE2eKeyId(_id, e2eKeyId, options) {
|
|
const query = {
|
|
_id,
|
|
};
|
|
|
|
const update = {
|
|
$set: {
|
|
e2eKeyId,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, options);
|
|
}
|
|
|
|
findOneByImportId(_id, options) {
|
|
const query = { importIds: _id };
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
findOneByName(name, options) {
|
|
const query = { name };
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
findOneByNameAndNotId(name, rid) {
|
|
const query = {
|
|
_id: { $ne: rid },
|
|
name,
|
|
};
|
|
|
|
return this.findOne(query);
|
|
}
|
|
|
|
findOneByDisplayName(fname, options) {
|
|
const query = { fname };
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
findOneByNameAndType(name, type, options) {
|
|
const query = {
|
|
name,
|
|
t: type,
|
|
};
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
// FIND
|
|
|
|
findById(roomId, options) {
|
|
return this.find({ _id: roomId }, options);
|
|
}
|
|
|
|
findByIds(roomIds, options) {
|
|
return this.find({ _id: { $in: [].concat(roomIds) } }, options);
|
|
}
|
|
|
|
findByType(type, options) {
|
|
const query = { t: type };
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findByTypeInIds(type, ids, options) {
|
|
const query = {
|
|
_id: {
|
|
$in: ids,
|
|
},
|
|
t: type,
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findByTypes(types, options) {
|
|
const query = {
|
|
t: {
|
|
$in: types,
|
|
},
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findByUserId(userId, options) {
|
|
const query = { 'u._id': userId };
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findBySubscriptionUserId(userId, options) {
|
|
const data = Subscriptions.findByUserId(userId, { fields: { rid: 1 } }).fetch()
|
|
.map((item) => item.rid);
|
|
|
|
const query = {
|
|
_id: {
|
|
$in: data,
|
|
},
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findBySubscriptionTypeAndUserId(type, userId, options) {
|
|
const data = Subscriptions.findByUserIdAndType(userId, type, { fields: { rid: 1 } }).fetch()
|
|
.map((item) => item.rid);
|
|
|
|
const query = {
|
|
t: type,
|
|
_id: {
|
|
$in: data,
|
|
},
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findBySubscriptionUserIdUpdatedAfter(userId, _updatedAt, options) {
|
|
const ids = Subscriptions.findByUserId(userId, { fields: { rid: 1 } }).fetch()
|
|
.map((item) => item.rid);
|
|
|
|
const query = {
|
|
_id: {
|
|
$in: ids,
|
|
},
|
|
_updatedAt: {
|
|
$gt: _updatedAt,
|
|
},
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findByNameContaining(name, options) {
|
|
const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i');
|
|
|
|
const query = {
|
|
$or: [
|
|
{ name: nameRegex },
|
|
{
|
|
t: 'd',
|
|
usernames: nameRegex,
|
|
},
|
|
],
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findByNameContainingAndTypes(name, types, options) {
|
|
const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i');
|
|
|
|
const query = {
|
|
t: {
|
|
$in: types,
|
|
},
|
|
$or: [
|
|
{ name: nameRegex },
|
|
{
|
|
t: 'd',
|
|
usernames: nameRegex,
|
|
},
|
|
],
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findByNameAndType(name, type, options) {
|
|
const query = {
|
|
t: type,
|
|
name,
|
|
};
|
|
|
|
// do not use cache
|
|
return this._db.find(query, options);
|
|
}
|
|
|
|
findByNameAndTypeNotDefault(name, type, options) {
|
|
const query = {
|
|
t: type,
|
|
name,
|
|
default: {
|
|
$ne: true,
|
|
},
|
|
};
|
|
|
|
// do not use cache
|
|
return this._db.find(query, options);
|
|
}
|
|
|
|
findByNameAndTypesNotInIds(name, types, ids, options) {
|
|
const query = {
|
|
_id: {
|
|
$ne: ids,
|
|
},
|
|
t: {
|
|
$in: types,
|
|
},
|
|
name,
|
|
};
|
|
|
|
// do not use cache
|
|
return this._db.find(query, options);
|
|
}
|
|
|
|
findChannelAndPrivateByNameStarting(name, options) {
|
|
const nameRegex = new RegExp(`^${ s.trim(s.escapeRegExp(name)) }`, 'i');
|
|
|
|
const query = {
|
|
t: {
|
|
$in: ['c', 'p'],
|
|
},
|
|
name: nameRegex,
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findByDefaultAndTypes(defaultValue, types, options) {
|
|
const query = {
|
|
default: defaultValue,
|
|
t: {
|
|
$in: types,
|
|
},
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findDirectRoomContainingUsername(username, options) {
|
|
const query = {
|
|
t: 'd',
|
|
usernames: username,
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findDirectRoomContainingAllUsernames(usernames, options) {
|
|
const query = {
|
|
t: 'd',
|
|
usernames: { $size: usernames.length, $all: usernames },
|
|
};
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
findByTypeAndName(type, name, options) {
|
|
const query = {
|
|
name,
|
|
t: type,
|
|
};
|
|
|
|
return this.findOne(query, options);
|
|
}
|
|
|
|
findByTypeAndNameContaining(type, name, options) {
|
|
const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i');
|
|
|
|
const query = {
|
|
name: nameRegex,
|
|
t: type,
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findByTypeInIdsAndNameContaining(type, ids, name, options) {
|
|
const nameRegex = new RegExp(s.trim(s.escapeRegExp(name)), 'i');
|
|
|
|
const query = {
|
|
_id: {
|
|
$in: ids,
|
|
},
|
|
name: nameRegex,
|
|
t: type,
|
|
};
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
findByTypeAndArchivationState(type, archivationstate, options) {
|
|
const query = { t: type };
|
|
|
|
if (archivationstate) {
|
|
query.archived = true;
|
|
} else {
|
|
query.archived = { $ne: true };
|
|
}
|
|
|
|
return this.find(query, options);
|
|
}
|
|
|
|
// UPDATE
|
|
addImportIds(_id, importIds) {
|
|
importIds = [].concat(importIds);
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$addToSet: {
|
|
importIds: {
|
|
$each: importIds,
|
|
},
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
archiveById(_id) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
archived: true,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
unarchiveById(_id) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
archived: false,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setNameById(_id, name, fname) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
name,
|
|
fname,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setFnameById(_id, fname) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
fname,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
incMsgCountById(_id, inc) {
|
|
if (inc == null) { inc = 1; }
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$inc: {
|
|
msgs: inc,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
incMsgCountAndSetLastMessageById(_id, inc, lastMessageTimestamp, lastMessage) {
|
|
if (inc == null) { inc = 1; }
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
lm: lastMessageTimestamp,
|
|
},
|
|
$inc: {
|
|
msgs: inc,
|
|
},
|
|
};
|
|
|
|
if (lastMessage) {
|
|
update.$set.lastMessage = lastMessage;
|
|
}
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
incUsersCountById(_id, inc = 1) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$inc: {
|
|
usersCount: inc,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
incUsersCountByIds(ids, inc = 1) {
|
|
const query = {
|
|
_id: {
|
|
$in: ids,
|
|
},
|
|
};
|
|
|
|
const update = {
|
|
$inc: {
|
|
usersCount: inc,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
setLastMessageById(_id, lastMessage) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
lastMessage,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
resetLastMessageById(_id, messageId) {
|
|
const query = { _id };
|
|
const lastMessage = Messages.getLastVisibleMessageSentWithNoTypeByRoomId(_id, messageId);
|
|
|
|
const update = lastMessage ? {
|
|
$set: {
|
|
lastMessage,
|
|
},
|
|
} : {
|
|
$unset: {
|
|
lastMessage: 1,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
replaceUsername(previousUsername, username) {
|
|
const query = { usernames: previousUsername };
|
|
|
|
const update = {
|
|
$set: {
|
|
'usernames.$': username,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
replaceMutedUsername(previousUsername, username) {
|
|
const query = { muted: previousUsername };
|
|
|
|
const update = {
|
|
$set: {
|
|
'muted.$': username,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
replaceUsernameOfUserByUserId(userId, username) {
|
|
const query = { 'u._id': userId };
|
|
|
|
const update = {
|
|
$set: {
|
|
'u.username': username,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update, { multi: true });
|
|
}
|
|
|
|
setJoinCodeById(_id, joinCode) {
|
|
let update;
|
|
const query = { _id };
|
|
|
|
if ((joinCode != null ? joinCode.trim() : undefined) !== '') {
|
|
update = {
|
|
$set: {
|
|
joinCodeRequired: true,
|
|
joinCode,
|
|
},
|
|
};
|
|
} else {
|
|
update = {
|
|
$set: {
|
|
joinCodeRequired: false,
|
|
},
|
|
$unset: {
|
|
joinCode: 1,
|
|
},
|
|
};
|
|
}
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setUserById(_id, user) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
u: {
|
|
_id: user._id,
|
|
username: user.username,
|
|
},
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setTypeById(_id, type) {
|
|
const query = { _id };
|
|
const update = {
|
|
$set: {
|
|
t: type,
|
|
},
|
|
};
|
|
if (type === 'p') {
|
|
update.$unset = { default: '' };
|
|
}
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setTopicById(_id, topic) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
topic,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setAnnouncementById(_id, announcement, announcementDetails) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
announcement,
|
|
announcementDetails,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setCustomFieldsById(_id, customFields) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
customFields,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
muteUsernameByRoomId(_id, username) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$addToSet: {
|
|
muted: username,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
unmuteUsernameByRoomId(_id, username) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$pull: {
|
|
muted: username,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
saveDefaultById(_id, defaultValue) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
default: defaultValue === 'true',
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
saveRetentionEnabledById(_id, value) {
|
|
const query = { _id };
|
|
|
|
const update = {};
|
|
|
|
if (value == null) {
|
|
update.$unset = { 'retention.enabled': true };
|
|
} else {
|
|
update.$set = { 'retention.enabled': !!value };
|
|
}
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
saveRetentionMaxAgeById(_id, value) {
|
|
const query = { _id };
|
|
|
|
value = Number(value);
|
|
if (!value) {
|
|
value = 30;
|
|
}
|
|
|
|
const update = {
|
|
$set: {
|
|
'retention.maxAge': value,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
saveRetentionExcludePinnedById(_id, value) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
'retention.excludePinned': value === true,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
saveRetentionFilesOnlyById(_id, value) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
'retention.filesOnly': value === true,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
saveRetentionOverrideGlobalById(_id, value) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
'retention.overrideGlobal': value === true,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
saveEncryptedById(_id, value) {
|
|
const query = { _id };
|
|
|
|
const update = {
|
|
$set: {
|
|
encrypted: value === true,
|
|
},
|
|
};
|
|
|
|
return this.update(query, update);
|
|
}
|
|
|
|
setTopicAndTagsById(_id, topic, tags) {
|
|
const setData = {};
|
|
const unsetData = {};
|
|
|
|
if (topic != null) {
|
|
if (!_.isEmpty(s.trim(topic))) {
|
|
setData.topic = s.trim(topic);
|
|
} else {
|
|
unsetData.topic = 1;
|
|
}
|
|
}
|
|
|
|
if (tags != null) {
|
|
if (!_.isEmpty(s.trim(tags))) {
|
|
setData.tags = s.trim(tags).split(',').map((tag) => s.trim(tag));
|
|
} else {
|
|
unsetData.tags = 1;
|
|
}
|
|
}
|
|
|
|
const update = {};
|
|
|
|
if (!_.isEmpty(setData)) {
|
|
update.$set = setData;
|
|
}
|
|
|
|
if (!_.isEmpty(unsetData)) {
|
|
update.$unset = unsetData;
|
|
}
|
|
|
|
if (_.isEmpty(update)) {
|
|
return;
|
|
}
|
|
|
|
return this.update({ _id }, update);
|
|
}
|
|
|
|
// INSERT
|
|
createWithTypeNameUserAndUsernames(type, name, fname, user, usernames, extraData) {
|
|
const room = {
|
|
name,
|
|
fname,
|
|
t: type,
|
|
usernames,
|
|
msgs: 0,
|
|
usersCount: 0,
|
|
u: {
|
|
_id: user._id,
|
|
username: user.username,
|
|
},
|
|
};
|
|
|
|
_.extend(room, extraData);
|
|
|
|
room._id = this.insert(room);
|
|
return room;
|
|
}
|
|
|
|
createWithIdTypeAndName(_id, type, name, extraData) {
|
|
const room = {
|
|
_id,
|
|
ts: new Date(),
|
|
t: type,
|
|
name,
|
|
usernames: [],
|
|
msgs: 0,
|
|
usersCount: 0,
|
|
};
|
|
|
|
_.extend(room, extraData);
|
|
|
|
this.insert(room);
|
|
return room;
|
|
}
|
|
|
|
createWithFullRoomData(room) {
|
|
delete room._id;
|
|
|
|
room._id = this.insert(room);
|
|
return room;
|
|
}
|
|
|
|
|
|
// REMOVE
|
|
removeById(_id) {
|
|
const query = { _id };
|
|
|
|
return this.remove(query);
|
|
}
|
|
|
|
removeDirectRoomContainingUsername(username) {
|
|
const query = {
|
|
t: 'd',
|
|
usernames: username,
|
|
};
|
|
|
|
return this.remove(query);
|
|
}
|
|
}
|
|
|
|
export default new Rooms('room', true);
|
|
|