diff --git a/.changeset/fuzzy-pans-share.md b/.changeset/fuzzy-pans-share.md deleted file mode 100644 index e689cb28df73139f9f9082743aada4f3e8bf6891..0000000000000000000000000000000000000000 --- a/.changeset/fuzzy-pans-share.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@rocket.chat/meteor": minor -"@rocket.chat/apps": minor -"@rocket.chat/core-typings": minor -"@rocket.chat/model-typings": minor ---- - -Adds a `source` field to livechat visitors, which stores the channel (eg API, widget, SMS, email-inbox, app) that's been used by the visitor to send messages. -Uses the new `source` field to assure each visitor is linked to a single source, so that each connection through a distinct channel creates a new visitor. diff --git a/apps/meteor/app/apps/server/bridges/livechat.ts b/apps/meteor/app/apps/server/bridges/livechat.ts index 8e9a2780820bb227234049f83dc55f32f78fd1cd..821d1fdd60d539c1908961293ccf616c3cac8302 100644 --- a/apps/meteor/app/apps/server/bridges/livechat.ts +++ b/apps/meteor/app/apps/server/bridges/livechat.ts @@ -52,21 +52,6 @@ export class AppLivechatBridge extends LivechatBridge { const appMessage = (await this.orch.getConverters().get('messages').convertAppMessage(message)) as IMessage | undefined; const livechatMessage = appMessage as ILivechatMessage | undefined; - if (guest) { - const visitorSource = { - type: OmnichannelSourceType.APP, - id: appId, - alias: this.orch.getManager()?.getOneById(appId)?.getNameSlug(), - }; - const fullVisitor = await LivechatVisitors.findOneEnabledByIdAndSource({ - _id: guest._id, - sourceFilter: { 'source.type': visitorSource.type, 'source.id': visitorSource.id, 'source.alias': visitorSource.alias }, - }); - if (!fullVisitor?.source) { - await LivechatVisitors.setSourceById(guest._id, visitorSource); - } - } - const msg = await LivechatTyped.sendMessage({ guest: guest as ILivechatVisitor, message: livechatMessage as ILivechatMessage, @@ -301,7 +286,7 @@ export class AppLivechatBridge extends LivechatBridge { } return Promise.all( - (await LivechatVisitors.findEnabledBySource({ 'source.type': OmnichannelSourceType.APP, 'source.id': appId }, query).toArray()).map( + (await LivechatVisitors.findEnabled(query).toArray()).map( async (visitor) => visitor && this.orch.getConverters()?.get('visitors').convertVisitor(visitor), ), ); @@ -310,7 +295,7 @@ export class AppLivechatBridge extends LivechatBridge { protected async findVisitorById(id: string, appId: string): Promise<IVisitor | undefined> { this.orch.debugLog(`The App ${appId} is looking for livechat visitors.`); - return this.orch.getConverters()?.get('visitors').convertByIdAndSource(id, appId); + return this.orch.getConverters()?.get('visitors').convertById(id); } protected async findVisitorByEmail(email: string, appId: string): Promise<IVisitor | undefined> { @@ -319,9 +304,7 @@ export class AppLivechatBridge extends LivechatBridge { return this.orch .getConverters() ?.get('visitors') - .convertVisitor( - await LivechatVisitors.findOneGuestByEmailAddressAndSource(email, { 'source.type': OmnichannelSourceType.APP, 'source.id': appId }), - ); + .convertVisitor(await LivechatVisitors.findOneGuestByEmailAddress(email)); } protected async findVisitorByToken(token: string, appId: string): Promise<IVisitor | undefined> { @@ -330,12 +313,7 @@ export class AppLivechatBridge extends LivechatBridge { return this.orch .getConverters() ?.get('visitors') - .convertVisitor( - await LivechatVisitors.getVisitorByTokenAndSource({ - token, - sourceFilter: { 'source.type': OmnichannelSourceType.APP, 'source.id': appId }, - }), - ); + .convertVisitor(await LivechatVisitors.getVisitorByToken(token, {})); } protected async findVisitorByPhoneNumber(phoneNumber: string, appId: string): Promise<IVisitor | undefined> { @@ -344,12 +322,7 @@ export class AppLivechatBridge extends LivechatBridge { return this.orch .getConverters() ?.get('visitors') - .convertVisitor( - await LivechatVisitors.findOneVisitorByPhoneAndSource(phoneNumber, { - 'source.type': OmnichannelSourceType.APP, - 'source.id': appId, - }), - ); + .convertVisitor(await LivechatVisitors.findOneVisitorByPhone(phoneNumber)); } protected async findDepartmentByIdOrName(value: string, appId: string): Promise<IDepartment | undefined> { diff --git a/apps/meteor/app/apps/server/converters/visitors.js b/apps/meteor/app/apps/server/converters/visitors.js index c70d7905bdbd53d5b8d167d4dce346c07d96a08a..32864e3e900e83409491844a0ae3f5b043c33eec 100644 --- a/apps/meteor/app/apps/server/converters/visitors.js +++ b/apps/meteor/app/apps/server/converters/visitors.js @@ -1,4 +1,3 @@ -import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors } from '@rocket.chat/models'; import { transformMappedData } from './transformMappedData'; @@ -15,30 +14,12 @@ export class AppVisitorsConverter { return this.convertVisitor(visitor); } - async convertByIdAndSource(id, appId) { - const visitor = await LivechatVisitors.findOneEnabledByIdAndSource({ - _id: id, - sourceFilter: { 'source.type': OmnichannelSourceType.APP, 'source.id': appId }, - }); - - return this.convertVisitor(visitor); - } - async convertByToken(token) { const visitor = await LivechatVisitors.getVisitorByToken(token); return this.convertVisitor(visitor); } - async convertByTokenAndSource(token, appId) { - const visitor = await LivechatVisitors.getVisitorByTokenAndSource({ - token, - sourceFilter: { 'source.type': OmnichannelSourceType.APP, 'source.id': appId }, - }); - - return this.convertVisitor(visitor); - } - async convertVisitor(visitor) { if (!visitor) { return undefined; @@ -56,7 +37,6 @@ export class AppVisitorsConverter { livechatData: 'livechatData', status: 'status', contactId: 'contactId', - source: 'source', }; return transformMappedData(visitor, map); @@ -76,7 +56,6 @@ export class AppVisitorsConverter { livechatData: visitor.livechatData, status: visitor.status || 'online', contactId: visitor.contactId, - source: visitor.source, ...(visitor.visitorEmails && { visitorEmails: visitor.visitorEmails }), ...(visitor.department && { department: visitor.department }), }; diff --git a/apps/meteor/app/livechat/imports/server/rest/sms.ts b/apps/meteor/app/livechat/imports/server/rest/sms.ts index 3004423c64ba98c712d2daf879d9e8501211406c..15f08cdc1e83bc27aade51d87c869cbeb088f8a5 100644 --- a/apps/meteor/app/livechat/imports/server/rest/sms.ts +++ b/apps/meteor/app/livechat/imports/server/rest/sms.ts @@ -6,7 +6,6 @@ import type { MessageAttachment, ServiceData, FileAttachmentProps, - IOmnichannelSource, } from '@rocket.chat/core-typings'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; @@ -56,24 +55,10 @@ const defineDepartment = async (idOrName?: string) => { return department?._id; }; -const defineVisitor = async (smsNumber: string, serviceName: string, destination: string, targetDepartment?: string) => { - const visitorSource: IOmnichannelSource = { - type: OmnichannelSourceType.SMS, - alias: serviceName, - }; - - const visitor = await LivechatVisitors.findOneVisitorByPhoneAndSource( - smsNumber, - { - 'source.type': visitorSource.type, - 'source.alias': visitorSource.alias, - }, - { projection: { token: 1 } }, - ); - visitorSource.destination = destination; - let data: { token: string; source: IOmnichannelSource; department?: string } = { +const defineVisitor = async (smsNumber: string, targetDepartment?: string) => { + const visitor = await LivechatVisitors.findOneVisitorByPhone(smsNumber); + let data: { token: string; department?: string } = { token: visitor?.token || Random.id(), - source: visitorSource, }; if (!visitor) { @@ -132,7 +117,7 @@ API.v1.addRoute('livechat/sms-incoming/:service', { targetDepartment = await defineDepartment(smsDepartment); } - const visitor = await defineVisitor(sms.from, service, sms.to, targetDepartment); + const visitor = await defineVisitor(sms.from, targetDepartment); if (!visitor) { return API.v1.success(SMSService.error(new Error('Invalid visitor'))); } diff --git a/apps/meteor/app/livechat/imports/server/rest/upload.ts b/apps/meteor/app/livechat/imports/server/rest/upload.ts index 30bb686fe06b0471bdb9093885a5c44b3b37572c..14db8f20afcf2c61ca5b69752fa1aa75df867b6f 100644 --- a/apps/meteor/app/livechat/imports/server/rest/upload.ts +++ b/apps/meteor/app/livechat/imports/server/rest/upload.ts @@ -1,9 +1,7 @@ -import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; import filesize from 'filesize'; import { API } from '../../../../api/server'; -import { isWidget } from '../../../../api/server/helpers/isWidget'; import { getUploadFormData } from '../../../../api/server/lib/getUploadFormData'; import { FileUpload } from '../../../../file-upload/server'; import { settings } from '../../../../settings/server'; @@ -15,7 +13,6 @@ API.v1.addRoute('livechat/upload/:rid', { if (!this.request.headers['x-visitor-token']) { return API.v1.unauthorized(); } - const sourceType = isWidget(this.request.headers) ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API; const canUpload = settings.get<boolean>('Livechat_fileupload_enabled') && settings.get<boolean>('FileUpload_Enabled'); @@ -26,10 +23,7 @@ API.v1.addRoute('livechat/upload/:rid', { } const visitorToken = this.request.headers['x-visitor-token']; - const visitor = await LivechatVisitors.getVisitorByTokenAndSource({ - token: visitorToken as string, - sourceFilter: { 'source.type': sourceType }, - }); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken as string, {}); if (!visitor) { return API.v1.unauthorized(); @@ -82,10 +76,6 @@ API.v1.addRoute('livechat/upload/:rid', { return API.v1.failure('Invalid file'); } - if (!visitor.source) { - await LivechatVisitors.setSourceById(visitor._id, { type: sourceType }); - } - uploadedFile.description = fields.description; delete fields.description; diff --git a/apps/meteor/app/livechat/server/api/lib/livechat.ts b/apps/meteor/app/livechat/server/api/lib/livechat.ts index 03aca9da02f30786cf06f8d23b27f48f1c22cd0c..01c4d9736c66a7f1ded6c9d223d64316b02f7116 100644 --- a/apps/meteor/app/livechat/server/api/lib/livechat.ts +++ b/apps/meteor/app/livechat/server/api/lib/livechat.ts @@ -1,11 +1,4 @@ -import type { - ILivechatAgent, - ILivechatDepartment, - ILivechatTrigger, - ILivechatVisitor, - IOmnichannelRoom, - OmnichannelSourceType, -} from '@rocket.chat/core-typings'; +import type { ILivechatAgent, ILivechatDepartment, ILivechatTrigger, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { License } from '@rocket.chat/license'; import { EmojiCustom, LivechatTrigger, LivechatVisitors, LivechatRooms, LivechatDepartment } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; @@ -69,21 +62,6 @@ export function findGuest(token: string): Promise<ILivechatVisitor | null> { }); } -export function findGuestBySource(token: string, sourceType: OmnichannelSourceType): Promise<ILivechatVisitor | null> { - const projection = { - name: 1, - username: 1, - token: 1, - visitorEmails: 1, - department: 1, - activity: 1, - contactId: 1, - source: 1, - }; - - return LivechatVisitors.getVisitorByTokenAndSource({ token, sourceFilter: { 'source.type': sourceType } }, { projection }); -} - export function findGuestWithoutActivity(token: string): Promise<ILivechatVisitor | null> { return LivechatVisitors.getVisitorByToken(token, { projection: { name: 1, username: 1, token: 1, visitorEmails: 1, department: 1 } }); } diff --git a/apps/meteor/app/livechat/server/api/v1/message.ts b/apps/meteor/app/livechat/server/api/v1/message.ts index bde332af5db42f17cec46937bdcf51d409a274ac..b7eb6e1f684a0b4fccbbc60458ee1195fd987b1e 100644 --- a/apps/meteor/app/livechat/server/api/v1/message.ts +++ b/apps/meteor/app/livechat/server/api/v1/message.ts @@ -1,4 +1,3 @@ -import type { IOmnichannelSource } from '@rocket.chat/core-typings'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms, Messages } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; @@ -19,7 +18,7 @@ import { loadMessageHistory } from '../../../../lib/server/functions/loadMessage import { settings } from '../../../../settings/server'; import { normalizeMessageFileUpload } from '../../../../utils/server/functions/normalizeMessageFileUpload'; import { Livechat as LivechatTyped } from '../../lib/LivechatTyped'; -import { findGuest, findGuestBySource, findRoom, normalizeHttpHeaderData } from '../lib/livechat'; +import { findGuest, findRoom, normalizeHttpHeaderData } from '../lib/livechat'; API.v1.addRoute( 'livechat/message', @@ -27,9 +26,8 @@ API.v1.addRoute( { async post() { const { token, rid, agent, msg } = this.bodyParams; - const sourceType = isWidget(this.request.headers) ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API; - const guest = await findGuestBySource(token, sourceType); + const guest = await findGuest(token); if (!guest) { throw new Error('invalid-token'); } @@ -50,10 +48,6 @@ API.v1.addRoute( throw new Error('message-length-exceeds-character-limit'); } - if (!guest.source) { - await LivechatVisitors.setSourceById(guest._id, { type: sourceType }); - } - const _id = this.bodyParams._id || Random.id(); const sendMessage = { @@ -67,7 +61,7 @@ API.v1.addRoute( agent, roomInfo: { source: { - type: sourceType, + type: isWidget(this.request.headers) ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API, }, }, }; @@ -256,12 +250,8 @@ API.v1.addRoute( { async post() { const visitorToken = this.bodyParams.visitor.token; - const sourceType = isWidget(this.request.headers) ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API; - const visitor = await LivechatVisitors.getVisitorByTokenAndSource( - { token: visitorToken, sourceFilter: { 'source.type': sourceType } }, - {}, - ); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken, {}); let rid: string; if (visitor) { const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {}); @@ -271,16 +261,11 @@ API.v1.addRoute( } else { rid = Random.id(); } - - if (!visitor.source) { - await LivechatVisitors.setSourceById(visitor._id, { type: sourceType }); - } } else { rid = Random.id(); - const guest: typeof this.bodyParams.visitor & { connectionData?: unknown; source?: IOmnichannelSource } = this.bodyParams.visitor; + const guest: typeof this.bodyParams.visitor & { connectionData?: unknown } = this.bodyParams.visitor; guest.connectionData = normalizeHttpHeaderData(this.request.headers); - guest.source = { type: sourceType }; const visitor = await LivechatTyped.registerGuest(guest); if (!visitor) { @@ -305,7 +290,7 @@ API.v1.addRoute( }, roomInfo: { source: { - type: sourceType, + type: isWidget(this.request.headers) ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API, }, }, }; diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 447a25987476d3814079c220ce8a640996f1f602..e521ac98fe711c0442b2ef13a58417bc7d1d42be 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -81,7 +81,7 @@ import { isDepartmentCreationAvailable } from './isDepartmentCreationAvailable'; import type { CloseRoomParams, CloseRoomParamsByUser, CloseRoomParamsByVisitor } from './localTypes'; import { parseTranscriptRequest } from './parseTranscriptRequest'; -type RegisterGuestType = Partial<Pick<ILivechatVisitor, 'token' | 'name' | 'department' | 'status' | 'username' | 'source'>> & { +type RegisterGuestType = Partial<Pick<ILivechatVisitor, 'token' | 'name' | 'department' | 'status' | 'username'>> & { id?: string; connectionData?: any; email?: string; @@ -654,7 +654,6 @@ class LivechatClass { username, connectionData, status = UserStatus.ONLINE, - source, }: RegisterGuestType): Promise<ILivechatVisitor | null> { check(token, String); check(id, Match.Maybe(String)); @@ -664,7 +663,6 @@ class LivechatClass { const visitorDataToUpdate: Partial<ILivechatVisitor> & { userAgent?: string; ip?: string; host?: string } = { token, status, - source, ...(phone?.number ? { phone: [{ phoneNumber: phone.number }] } : {}), ...(name ? { name } : {}), }; @@ -710,7 +708,6 @@ class LivechatClass { visitorDataToUpdate.username = username || (await LivechatVisitors.getNextVisitorUsername()); visitorDataToUpdate.status = status; visitorDataToUpdate.ts = new Date(); - visitorDataToUpdate.source = source; if (settings.get('Livechat_Allow_collect_and_store_HTTP_header_informations') && Livechat.isValidObject(connectionData)) { Livechat.logger.debug(`Saving connection data for visitor ${token}`); diff --git a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts index badf4149081f6855b7c7cfc594a30c2c3eb76255..6fac80397906fd7e8e101176e974dedc57ec49d4 100644 --- a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts +++ b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts @@ -42,27 +42,19 @@ export const sendMessageLivechat = async ({ }), ); - const guest = await LivechatVisitors.getVisitorByTokenAndSource( - { token, sourceFilter: { 'source.type': { $in: [OmnichannelSourceType.API, OmnichannelSourceType.WIDGET] } } }, - { - projection: { - name: 1, - username: 1, - department: 1, - token: 1, - source: 1, - }, + const guest = await LivechatVisitors.getVisitorByToken(token, { + projection: { + name: 1, + username: 1, + department: 1, + token: 1, }, - ); + }); if (!guest) { throw new Meteor.Error('invalid-token'); } - if (!guest.source) { - await LivechatVisitors.setSourceById(guest._id, { type: OmnichannelSourceType.API }); - } - if (settings.get('Livechat_enable_message_character_limit') && msg.length > parseInt(settings.get('Livechat_message_character_limit'))) { throw new Meteor.Error('message-length-exceeds-character-limit'); } diff --git a/apps/meteor/app/livechat/server/sendMessageBySMS.ts b/apps/meteor/app/livechat/server/sendMessageBySMS.ts index 57013508673eb439b23903cd24d5f29068a17502..c7f88646158b92268029305bfe4af9ba4f9a2c77 100644 --- a/apps/meteor/app/livechat/server/sendMessageBySMS.ts +++ b/apps/meteor/app/livechat/server/sendMessageBySMS.ts @@ -1,6 +1,5 @@ import { OmnichannelIntegration } from '@rocket.chat/core-services'; -import type { IOmnichannelSource } from '@rocket.chat/core-typings'; -import { isEditedMessage, OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatVisitors } from '@rocket.chat/models'; import { callbacks } from '../../../lib/callbacks'; @@ -56,20 +55,11 @@ callbacks.add( return message; } - const visitorSource: IOmnichannelSource = { type: OmnichannelSourceType.SMS, alias: service }; - const visitor = await LivechatVisitors.getVisitorByTokenAndSource( - { token: room.v.token, sourceFilter: { 'source.type': visitorSource.type, 'source.alias': visitorSource.alias } }, - { projection: { phone: 1, source: 1 } }, - ); + const visitor = await LivechatVisitors.getVisitorByToken(room.v.token, { projection: { phone: 1 } }); if (!visitor?.phone || visitor.phone.length === 0) { return message; } - visitorSource.destination = visitor.phone[0].phoneNumber; - - if (!visitor.source) { - await LivechatVisitors.setSourceById(visitor._id, visitorSource); - } try { await SMSService.send(room.sms.from, visitor.phone[0].phoneNumber, message.msg, extraData); diff --git a/apps/meteor/ee/server/apps/communication/uikit.ts b/apps/meteor/ee/server/apps/communication/uikit.ts index 87bb37965d0751eebab947da9c7d9b9b199e2ba4..0392076704d72db7183e2696c3f2e5611a95ddae 100644 --- a/apps/meteor/ee/server/apps/communication/uikit.ts +++ b/apps/meteor/ee/server/apps/communication/uikit.ts @@ -61,10 +61,9 @@ router.use(authenticationMiddleware({ rejectUnauthorized: false })); router.use(async (req: Request, res, next) => { const { 'x-visitor-token': visitorToken } = req.headers; - const { id: appId } = req.params; if (visitorToken) { - req.body.visitor = await Apps.getConverters()?.get('visitors').convertByTokenAndSource(visitorToken, appId); + req.body.visitor = await Apps.getConverters()?.get('visitors').convertByToken(visitorToken); } if (!req.user && !req.body.visitor) { diff --git a/apps/meteor/server/features/EmailInbox/EmailInbox_Incoming.ts b/apps/meteor/server/features/EmailInbox/EmailInbox_Incoming.ts index 907f84998d062a624b49a71ad0b23a4d998e976a..7ecb8f309b29d8c6944c6bb5d13603d1f6c69ede 100644 --- a/apps/meteor/server/features/EmailInbox/EmailInbox_Incoming.ts +++ b/apps/meteor/server/features/EmailInbox/EmailInbox_Incoming.ts @@ -24,15 +24,8 @@ type FileAttachment = VideoAttachmentProps & ImageAttachmentProps & AudioAttachm const language = settings.get<string>('Language') || 'en'; const t = i18n.getFixedT(language); -async function getGuestByEmail(email: string, name: string, inbox: string, department = ''): Promise<ILivechatVisitor | null> { - const guest = await LivechatVisitors.findOneGuestByEmailAddressAndSource( - email, - { - 'source.type': OmnichannelSourceType.EMAIL, - 'source.id': inbox, - }, - { projection: { department: 1, token: 1, source: 1 } }, - ); +async function getGuestByEmail(email: string, name: string, department = ''): Promise<ILivechatVisitor | null> { + const guest = await LivechatVisitors.findOneGuestByEmailAddress(email); if (guest) { if (guest.department !== department) { @@ -44,10 +37,6 @@ async function getGuestByEmail(email: string, name: string, inbox: string, depar await LivechatTyped.setDepartmentForGuest({ token: guest.token, department }); return LivechatVisitors.findOneEnabledById(guest._id, {}); } - if (!guest.source) { - const source = { type: OmnichannelSourceType.EMAIL, id: inbox, alias: 'email-inbox' }; - await LivechatVisitors.setSourceById(guest._id, source); - } return guest; } @@ -56,7 +45,6 @@ async function getGuestByEmail(email: string, name: string, inbox: string, depar name: name || email, email, department, - source: { type: OmnichannelSourceType.EMAIL, id: inbox, alias: 'email-inbox' }, }); if (!livechatVisitor) { @@ -117,7 +105,7 @@ export async function onEmailReceived(email: ParsedMail, inbox: string, departme const references = typeof email.references === 'string' ? [email.references] : email.references; const initialRef = [email.messageId, email.inReplyTo].filter(Boolean) as string[]; const thread = (references?.length ? references : []).flatMap((t: string) => t.split(',')).concat(initialRef); - const guest = await getGuestByEmail(email.from.value[0].address, email.from.value[0].name, inbox, department); + const guest = await getGuestByEmail(email.from.value[0].address, email.from.value[0].name, department); if (!guest) { logger.error(`No visitor found for ${email.from.value[0].address}`); diff --git a/apps/meteor/server/models/raw/LivechatVisitors.ts b/apps/meteor/server/models/raw/LivechatVisitors.ts index 1c371449fd93a7737266fcc74aaa1b147066299d..396b728159ff63e6e03d96d0876c37f3f03649fe 100644 --- a/apps/meteor/server/models/raw/LivechatVisitors.ts +++ b/apps/meteor/server/models/raw/LivechatVisitors.ts @@ -22,7 +22,6 @@ import { ObjectId } from 'mongodb'; import { notifyOnSettingChanged } from '../../../app/lib/server/lib/notifyListener'; import { BaseRaw } from './BaseRaw'; -const emptySourceFilter = { source: { $exists: false } }; export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements ILivechatVisitorsModel { constructor(db: Db, trash?: Collection<RocketChatRecordDeleted<ILivechatVisitor>>) { super(db, 'livechat_visitor', trash); @@ -50,19 +49,6 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL return this.findOne(query); } - findOneVisitorByPhoneAndSource( - phone: string, - sourceFilter: Filter<ILivechatVisitor>, - options?: FindOptions<ILivechatVisitor>, - ): Promise<ILivechatVisitor | null> { - const query = { - 'phone.phoneNumber': phone, - ...(sourceFilter ? { $or: [sourceFilter, emptySourceFilter] } : emptySourceFilter), - }; - - return this.findOne(query, options); - } - findOneGuestByEmailAddress(emailAddress: string): Promise<ILivechatVisitor | null> { const query = { 'visitorEmails.address': String(emailAddress).toLowerCase(), @@ -71,19 +57,6 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL return this.findOne(query); } - findOneGuestByEmailAddressAndSource( - emailAddress: string, - sourceFilter: Filter<ILivechatVisitor>, - options?: FindOptions<ILivechatVisitor>, - ): Promise<ILivechatVisitor | null> { - const query = { - 'visitorEmails.address': emailAddress.toLowerCase(), - ...(sourceFilter ? { $or: [sourceFilter, emptySourceFilter] } : emptySourceFilter), - }; - - return this.findOne(query, options); - } - /** * Find visitors by _id * @param {string} token - Visitor token @@ -96,15 +69,10 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL return this.find(query, options); } - findEnabledBySource( - sourceFilter: Filter<ILivechatVisitor>, - query: Filter<ILivechatVisitor>, - options?: FindOptions<ILivechatVisitor>, - ): FindCursor<ILivechatVisitor> { + findEnabled(query: Filter<ILivechatVisitor>, options?: FindOptions<ILivechatVisitor>): FindCursor<ILivechatVisitor> { return this.find( { ...query, - $or: [sourceFilter, emptySourceFilter], disabled: { $ne: true }, }, options, @@ -120,19 +88,6 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL return this.findOne<T>(query, options); } - findOneEnabledByIdAndSource<T extends Document = ILivechatVisitor>( - { _id, sourceFilter }: { _id: string; sourceFilter: Filter<ILivechatVisitor> }, - options?: FindOptions<ILivechatVisitor>, - ): Promise<T | null> { - const query = { - _id, - disabled: { $ne: true }, - ...(sourceFilter ? { $or: [sourceFilter, emptySourceFilter] } : emptySourceFilter), - }; - - return this.findOne<T>(query, options); - } - findVisitorByToken(token: string): FindCursor<ILivechatVisitor> { const query = { token, @@ -150,18 +105,6 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL return this.findOne(query, options); } - getVisitorByTokenAndSource( - { token, sourceFilter }: { token: string; sourceFilter?: Filter<ILivechatVisitor> }, - options: FindOptions<ILivechatVisitor>, - ): Promise<ILivechatVisitor | null> { - const query = { - token, - ...(sourceFilter ? { $or: [sourceFilter, emptySourceFilter] } : emptySourceFilter), - }; - - return this.findOne(query, options); - } - getVisitorsBetweenDate({ start, end, department }: { start: Date; end: Date; department?: string }): FindCursor<ILivechatVisitor> { const query = { disabled: { $ne: true }, @@ -527,17 +470,6 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL }, ); } - - setSourceById(_id: string, source: Required<ILivechatVisitor['source']>): Promise<UpdateResult> { - return this.updateOne( - { _id }, - { - $set: { - source, - }, - }, - ); - } } type DeepWriteable<T> = { -readonly [P in keyof T]: DeepWriteable<T[P]> }; diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index e67e30d46f09cfd3cfe7c0516919b4816735668b..43454c5115dc9538006b47ae0a82306700f31b6d 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -13,7 +13,7 @@ import type { } from '@rocket.chat/core-typings'; import { LivechatPriorityWeight } from '@rocket.chat/core-typings'; import { expect } from 'chai'; -import { after, afterEach, before, describe, it } from 'mocha'; +import { after, before, describe, it } from 'mocha'; import type { Response } from 'supertest'; import type { SuccessResult } from '../../../../app/api/server/definition'; @@ -1150,19 +1150,6 @@ describe('LIVECHAT - rooms', () => { }); describe('livechat/upload/:rid', () => { - let visitor: ILivechatVisitor | undefined; - - afterEach(() => { - if (visitor?.token) { - return deleteVisitor(visitor.token); - } - }); - - after(async () => { - await updateSetting('FileUpload_Enabled', true); - await updateSetting('Livechat_fileupload_enabled', true); - }); - it('should throw an error if x-visitor-token header is not present', async () => { await request .post(api('livechat/upload/test')) @@ -1183,7 +1170,7 @@ describe('LIVECHAT - rooms', () => { }); it('should throw unauthorized if visitor with token exists but room is invalid', async () => { - visitor = await createVisitor(); + const visitor = await createVisitor(); await request .post(api('livechat/upload/test')) .set(credentials) @@ -1191,10 +1178,11 @@ describe('LIVECHAT - rooms', () => { .attach('file', fs.createReadStream(path.join(__dirname, '../../../data/livechat/sample.png'))) .expect('Content-Type', 'application/json') .expect(403); + await deleteVisitor(visitor.token); }); it('should throw an error if the file is not attached', async () => { - visitor = await createVisitor(); + const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); await request .post(api(`livechat/upload/${room._id}`)) @@ -1202,11 +1190,12 @@ describe('LIVECHAT - rooms', () => { .set('x-visitor-token', visitor.token) .expect('Content-Type', 'application/json') .expect(400); + await deleteVisitor(visitor.token); }); it('should throw and error if file uploads are enabled but livechat file uploads are disabled', async () => { await updateSetting('Livechat_fileupload_enabled', false); - visitor = await createVisitor(); + const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); await request .post(api(`livechat/upload/${room._id}`)) @@ -1216,11 +1205,12 @@ describe('LIVECHAT - rooms', () => { .expect('Content-Type', 'application/json') .expect(400); await updateSetting('Livechat_fileupload_enabled', true); + await deleteVisitor(visitor.token); }); it('should throw and error if livechat file uploads are enabled but file uploads are disabled', async () => { await updateSetting('FileUpload_Enabled', false); - visitor = await createVisitor(); + const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); await request .post(api(`livechat/upload/${room._id}`)) @@ -1230,12 +1220,13 @@ describe('LIVECHAT - rooms', () => { .expect('Content-Type', 'application/json') .expect(400); await updateSetting('FileUpload_Enabled', true); + await deleteVisitor(visitor.token); }); it('should throw and error if both file uploads are disabled', async () => { await updateSetting('Livechat_fileupload_enabled', false); await updateSetting('FileUpload_Enabled', false); - visitor = await createVisitor(); + const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); await request .post(api(`livechat/upload/${room._id}`)) @@ -1246,26 +1237,14 @@ describe('LIVECHAT - rooms', () => { .expect(400); await updateSetting('FileUpload_Enabled', true); await updateSetting('Livechat_fileupload_enabled', true); - }); - it('should upload an image on the room if all params are valid', async () => { - await updateSetting('FileUpload_Enabled', true); - await updateSetting('Livechat_fileupload_enabled', true); - visitor = await createVisitor(); - const room = await createLivechatRoom(visitor.token); - await request - .post(api(`livechat/upload/${room._id}`)) - .set(credentials) - .set('x-visitor-token', visitor.token) - .attach('file', fs.createReadStream(path.join(__dirname, '../../../data/livechat/sample.png'))) - .expect('Content-Type', 'application/json') - .expect(200); + await deleteVisitor(visitor.token); }); - it("should set visitor's source as API after uploading a file", async () => { + it('should upload an image on the room if all params are valid', async () => { await updateSetting('FileUpload_Enabled', true); await updateSetting('Livechat_fileupload_enabled', true); - visitor = await createVisitor(); + const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); await request .post(api(`livechat/upload/${room._id}`)) @@ -1274,20 +1253,11 @@ describe('LIVECHAT - rooms', () => { .attach('file', fs.createReadStream(path.join(__dirname, '../../../data/livechat/sample.png'))) .expect('Content-Type', 'application/json') .expect(200); - - const { body } = await request - .get(api('livechat/visitors.info')) - .query({ visitorId: visitor._id }) - .set(credentials) - .expect('Content-Type', 'application/json') - .expect(200); - expect(body).to.have.property('visitor').and.to.be.an('object'); - expect(body.visitor).to.have.property('source').and.to.be.an('object'); - expect(body.visitor.source).to.have.property('type', 'api'); + await deleteVisitor(visitor.token); }); it('should allow visitor to download file', async () => { - visitor = await createVisitor(); + const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); const { body } = await request @@ -1302,11 +1272,12 @@ describe('LIVECHAT - rooms', () => { } = body; const imageUrl = `/file-upload/${_id}/${name}`; await request.get(imageUrl).query({ rc_token: visitor.token, rc_room_type: 'l', rc_rid: room._id }).expect(200); + await deleteVisitor(visitor.token); await closeOmnichannelRoom(room._id); }); it('should allow visitor to download file even after room is closed', async () => { - visitor = await createVisitor(); + const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); const { body } = await request .post(api(`livechat/upload/${room._id}`)) @@ -1321,10 +1292,11 @@ describe('LIVECHAT - rooms', () => { } = body; const imageUrl = `/file-upload/${_id}/${name}`; await request.get(imageUrl).query({ rc_token: visitor.token, rc_room_type: 'l', rc_rid: room._id }).expect(200); + await deleteVisitor(visitor.token); }); it('should not allow visitor to download a file from a room he didnt create', async () => { - visitor = await createVisitor(); + const visitor = await createVisitor(); const visitor2 = await createVisitor(); const room = await createLivechatRoom(visitor.token); const { body } = await request @@ -1341,6 +1313,7 @@ describe('LIVECHAT - rooms', () => { } = body; const imageUrl = `/file-upload/${_id}/${name}`; await request.get(imageUrl).query({ rc_token: visitor2.token, rc_room_type: 'l', rc_rid: room._id }).expect(403); + await deleteVisitor(visitor.token); await deleteVisitor(visitor2.token); }); }); @@ -1678,33 +1651,6 @@ describe('LIVECHAT - rooms', () => { expect(body.messages[1]).to.have.property('username', visitor.username); await deleteVisitor(visitor.token); }); - - it("should set visitor's source as API after creating messages in a room", async () => { - const visitor = await createVisitor(); - const room = await createLivechatRoom(visitor.token); - await sendMessage(room._id, 'Hello', visitor.token); - - const { body } = await request - .post(api('livechat/messages')) - .set(credentials) - .send({ visitor: { token: visitor.token }, messages: [{ msg: 'Hello' }, { msg: 'Hello 2' }] }) - .expect('Content-Type', 'application/json') - .expect(200); - expect(body).to.have.property('success', true); - expect(body).to.have.property('messages').of.length(2); - - const { body: getVisitorResponse } = await request - .get(api('livechat/visitors.info')) - .query({ visitorId: visitor._id }) - .set(credentials) - .expect('Content-Type', 'application/json') - .expect(200); - - expect(getVisitorResponse).to.have.property('visitor').and.to.be.an('object'); - expect(getVisitorResponse.visitor).to.have.property('source').and.to.be.an('object'); - expect(getVisitorResponse.visitor.source).to.have.property('type', 'api'); - await deleteVisitor(visitor.token); - }); }); describe('livechat/transfer.history/:rid', () => { diff --git a/apps/meteor/tests/end-to-end/api/livechat/06-integrations.ts b/apps/meteor/tests/end-to-end/api/livechat/06-integrations.ts index 54b888645a73c3ec11f24a8853568e3cad79d4c7..bc4d6fa04dc419302ce65fcacd425b805bf04e55 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/06-integrations.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/06-integrations.ts @@ -1,10 +1,9 @@ import type { ISetting } from '@rocket.chat/core-typings'; import { expect } from 'chai'; -import { after, before, describe, it } from 'mocha'; +import { before, describe, it } from 'mocha'; import type { Response } from 'supertest'; import { getCredentials, api, request, credentials } from '../../../data/api-data'; -import { deleteVisitor } from '../../../data/livechat/rooms'; import { updatePermission, updateSetting } from '../../../data/permissions.helper'; describe('LIVECHAT - Integrations', () => { @@ -50,19 +49,11 @@ describe('LIVECHAT - Integrations', () => { }); describe('Incoming SMS', () => { - const visitorTokens: string[] = []; - before(async () => { await updateSetting('SMS_Enabled', true); await updateSetting('SMS_Service', ''); }); - after(async () => { - await updateSetting('SMS_Default_Omnichannel_Department', ''); - await updateSetting('SMS_Service', 'twilio'); - return Promise.all(visitorTokens.map((token) => deleteVisitor(token))); - }); - describe('POST livechat/sms-incoming/:service', () => { it('should throw an error if SMS is disabled', async () => { await updateSetting('SMS_Enabled', false); @@ -124,47 +115,6 @@ describe('LIVECHAT - Integrations', () => { expect(res).to.have.property('text', '<Response></Response>'); }); }); - - it("should set visitor's source as SMS after sending a message", async () => { - await updateSetting('SMS_Default_Omnichannel_Department', ''); - await updateSetting('SMS_Service', 'twilio'); - - const token = `${new Date().getTime()}-test2`; - const phone = new Date().getTime().toString(); - const { body: createVisitorResponse } = await request.post(api('livechat/visitor')).send({ visitor: { token, phone } }); - expect(createVisitorResponse).to.have.property('success', true); - expect(createVisitorResponse).to.have.property('visitor').and.to.be.an('object'); - expect(createVisitorResponse.visitor).to.have.property('_id'); - const visitorId = createVisitorResponse.visitor._id; - visitorTokens.push(createVisitorResponse.visitor.token); - - await request - .post(api('livechat/sms-incoming/twilio')) - .set(credentials) - .send({ - From: phone, - To: '+123456789', - Body: 'Hello', - }) - .expect('Content-Type', 'text/xml') - .expect(200) - .expect((res: Response) => { - expect(res).to.have.property('text', '<Response></Response>'); - }); - - const { body } = await request - .get(api('livechat/visitors.info')) - .query({ visitorId }) - .set(credentials) - .expect('Content-Type', 'application/json') - .expect(200); - - expect(body).to.have.property('visitor').and.to.be.an('object'); - expect(body.visitor).to.have.property('source').and.to.be.an('object'); - expect(body.visitor.source).to.have.property('type', 'sms'); - expect(body.visitor.source).to.have.property('alias', 'twilio'); - expect(body.visitor.source).to.have.property('destination', '+123456789'); - }); }); }); diff --git a/apps/meteor/tests/end-to-end/api/livechat/11-livechat.ts b/apps/meteor/tests/end-to-end/api/livechat/11-livechat.ts index b5f237832f598d53ef3af9c128da3fa08d1c8019..7ce582025538a6dc0fc1eeb4a77a69750876d839 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/11-livechat.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/11-livechat.ts @@ -516,10 +516,6 @@ describe('LIVECHAT - Utils', () => { }); describe('livechat/message', () => { - const visitorTokens: string[] = []; - - after(() => Promise.all(visitorTokens.map((token) => deleteVisitor(token)))); - it('should fail if no token', async () => { await request.post(api('livechat/message')).set(credentials).send({}).expect(400); }); @@ -534,29 +530,22 @@ describe('LIVECHAT - Utils', () => { }); it('should fail if rid is invalid', async () => { const visitor = await createVisitor(); - visitorTokens.push(visitor.token); await request.post(api('livechat/message')).set(credentials).send({ token: visitor.token, rid: 'test', msg: 'test' }).expect(400); }); it('should fail if rid belongs to another visitor', async () => { const visitor = await createVisitor(); const visitor2 = await createVisitor(); - visitorTokens.push(visitor.token, visitor2.token); - const room = await createLivechatRoom(visitor2.token); await request.post(api('livechat/message')).set(credentials).send({ token: visitor.token, rid: room._id, msg: 'test' }).expect(400); }); it('should fail if room is closed', async () => { const visitor = await createVisitor(); - visitorTokens.push(visitor.token); - const room = await createLivechatRoom(visitor.token); await closeOmnichannelRoom(room._id); await request.post(api('livechat/message')).set(credentials).send({ token: visitor.token, rid: room._id, msg: 'test' }).expect(400); }); it('should fail if message is greater than Livechat_enable_message_character_limit setting', async () => { const visitor = await createVisitor(); - visitorTokens.push(visitor.token); - const room = await createLivechatRoom(visitor.token); await updateSetting('Livechat_enable_message_character_limit', true); await updateSetting('Livechat_message_character_limit', 1); @@ -566,29 +555,9 @@ describe('LIVECHAT - Utils', () => { }); it('should send a message', async () => { const visitor = await createVisitor(); - visitorTokens.push(visitor.token); - const room = await createLivechatRoom(visitor.token); await request.post(api('livechat/message')).set(credentials).send({ token: visitor.token, rid: room._id, msg: 'test' }).expect(200); }); - it("should set visitor's source as API after sending a message", async () => { - const visitor = await createVisitor(); - visitorTokens.push(visitor.token); - - const room = await createLivechatRoom(visitor.token); - await request.post(api('livechat/message')).set(credentials).send({ token: visitor.token, rid: room._id, msg: 'test' }).expect(200); - - const { body } = await request - .get(api('livechat/visitors.info')) - .query({ visitorId: visitor._id }) - .set(credentials) - .expect('Content-Type', 'application/json') - .expect(200); - - expect(body).to.have.property('visitor').and.to.be.an('object'); - expect(body.visitor).to.have.property('source').and.to.be.an('object'); - expect(body.visitor.source).to.have.property('type', 'api'); - }); }); describe('[GET] livechat/message/:_id', () => { diff --git a/packages/apps/src/converters/IAppVisitorsConverter.ts b/packages/apps/src/converters/IAppVisitorsConverter.ts index 5d359888155f04b7a61b9ecdca3699b8fd8a9125..575845b57c105764fa3e743b38e170819364bba4 100644 --- a/packages/apps/src/converters/IAppVisitorsConverter.ts +++ b/packages/apps/src/converters/IAppVisitorsConverter.ts @@ -4,9 +4,7 @@ import type { IAppsVisitor } from '../AppsEngine'; export interface IAppVisitorsConverter { convertById(visitorId: ILivechatVisitor['_id']): Promise<IAppsVisitor | undefined>; - convertByIdAndSource(visitorId: ILivechatVisitor['_id'], appId: string): Promise<IAppsVisitor | undefined>; convertByToken(token: string): Promise<IAppsVisitor | undefined>; - convertByTokenAndSource(token: string, appId: string): Promise<IAppsVisitor | undefined>; convertVisitor(visitor: undefined | null): Promise<undefined>; convertVisitor(visitor: ILivechatVisitor): Promise<IAppsVisitor>; convertVisitor(visitor: ILivechatVisitor | undefined | null): Promise<IAppsVisitor | undefined>; diff --git a/packages/core-typings/src/ILivechatVisitor.ts b/packages/core-typings/src/ILivechatVisitor.ts index 3dc91911260df540003b92fd7a66bb110d326fd4..eefb4ebd720c8f2121584ea59879b1f041270e67 100644 --- a/packages/core-typings/src/ILivechatVisitor.ts +++ b/packages/core-typings/src/ILivechatVisitor.ts @@ -1,5 +1,4 @@ import type { IRocketChatRecord } from './IRocketChatRecord'; -import type { IOmnichannelSource } from './IRoom'; import type { UserStatus } from './UserStatus'; export interface IVisitorPhone { @@ -51,7 +50,6 @@ export interface ILivechatVisitor extends IRocketChatRecord { activity?: string[]; disabled?: boolean; contactId?: string; - source?: IOmnichannelSource; } export interface ILivechatVisitorDTO { diff --git a/packages/model-typings/src/models/ILivechatVisitorsModel.ts b/packages/model-typings/src/models/ILivechatVisitorsModel.ts index 7b47a1b3f0cc21853cfb454db2f2fe4dfc16571f..3e17fc2a59624fd3b6388dae0384bd79d2c794f8 100644 --- a/packages/model-typings/src/models/ILivechatVisitorsModel.ts +++ b/packages/model-typings/src/models/ILivechatVisitorsModel.ts @@ -16,10 +16,6 @@ import type { FindPaginated, IBaseModel } from './IBaseModel'; export interface ILivechatVisitorsModel extends IBaseModel<ILivechatVisitor> { findById(_id: string, options?: FindOptions<ILivechatVisitor>): FindCursor<ILivechatVisitor>; getVisitorByToken(token: string, options?: FindOptions<ILivechatVisitor>): Promise<ILivechatVisitor | null>; - getVisitorByTokenAndSource( - { token, sourceFilter }: { token: string; sourceFilter?: Filter<ILivechatVisitor> }, - options?: FindOptions<ILivechatVisitor>, - ): Promise<ILivechatVisitor | null>; getVisitorsBetweenDate({ start, end, department }: { start: Date; end: Date; department?: string }): FindCursor<ILivechatVisitor>; findByNameRegexWithExceptionsAndConditions<P extends Document = ILivechatVisitor>( searchTerm: string, @@ -51,20 +47,8 @@ export interface ILivechatVisitorsModel extends IBaseModel<ILivechatVisitor> { findOneGuestByEmailAddress(emailAddress: string): Promise<ILivechatVisitor | null>; - findOneGuestByEmailAddressAndSource( - emailAddress: string, - sourceFilter: Filter<ILivechatVisitor>, - options?: FindOptions<ILivechatVisitor>, - ): Promise<ILivechatVisitor | null>; - findOneVisitorByPhone(phone: string): Promise<ILivechatVisitor | null>; - findOneVisitorByPhoneAndSource( - phone: string, - sourceFilter: Filter<ILivechatVisitor>, - options?: FindOptions<ILivechatVisitor>, - ): Promise<ILivechatVisitor | null>; - removeDepartmentById(_id: string): Promise<Document | UpdateResult>; getNextVisitorUsername(): Promise<string>; @@ -83,18 +67,9 @@ export interface ILivechatVisitorsModel extends IBaseModel<ILivechatVisitor> { findOneEnabledById<T extends Document = ILivechatVisitor>(_id: string, options?: FindOptions<ILivechatVisitor>): Promise<T | null>; - findOneEnabledByIdAndSource<T extends Document = ILivechatVisitor>( - { _id, sourceFilter }: { _id: string; sourceFilter: Filter<ILivechatVisitor> }, - options?: FindOptions<ILivechatVisitor>, - ): Promise<T | null>; - disableById(_id: string): Promise<UpdateResult>; - findEnabledBySource( - sourceFilter: Filter<ILivechatVisitor>, - query: Filter<ILivechatVisitor>, - options?: FindOptions<ILivechatVisitor>, - ): FindCursor<ILivechatVisitor>; + findEnabled(query: Filter<ILivechatVisitor>, options?: FindOptions<ILivechatVisitor>): FindCursor<ILivechatVisitor>; countVisitorsOnPeriod(period: string): Promise<number>; saveGuestById( @@ -102,5 +77,4 @@ export interface ILivechatVisitorsModel extends IBaseModel<ILivechatVisitor> { data: { name?: string; username?: string; email?: string; phone?: string; livechatData: { [k: string]: any } }, ): Promise<UpdateResult | Document | boolean>; setLastChatById(_id: string, lastChat: Required<ILivechatVisitor['lastChat']>): Promise<UpdateResult>; - setSourceById(_id: string, source: ILivechatVisitor['source']): Promise<UpdateResult>; }