snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
657
packages/omni-core/CHANGELOG.md
Normal file
657
packages/omni-core/CHANGELOG.md
Normal file
@@ -0,0 +1,657 @@
|
||||
# @rocket.chat/omni-core
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [4947601bbf042cd1b2385f8f5dda438e608faea7, b89a8d411ef65f6931a5fd1cd057740bc00cd9ba, 5deefe2766132f13118f38dc42cf1c9895208961, 8984df841e1ecc46198e073982adabc1f069c352, a9f3e6cd3f31eac17bec626287fcf815d802e9b9, 742009a09148e33d141f50691cdec0f7e9818535]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.5.0
|
||||
</details>
|
||||
|
||||
## 0.1.4-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [4947601bbf042cd1b2385f8f5dda438e608faea7, b89a8d411ef65f6931a5fd1cd057740bc00cd9ba, 5deefe2766132f13118f38dc42cf1c9895208961, 8984df841e1ecc46198e073982adabc1f069c352, a9f3e6cd3f31eac17bec626287fcf815d802e9b9, 742009a09148e33d141f50691cdec0f7e9818535]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.5.0-rc.0
|
||||
- @rocket.chat/patch-injection@0.0.2
|
||||
</details>
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [c7aff48a40a9a78924cbf27fd38930c536ee11e5, 5f92f9a27dca70d506d919351612bd32dc04241a, 13b4a7b2dc203959b77b3b0c5f154d3e34fe2058, 4b34bd62f2ac8d51efd2f48caea7092e87f30ce7, ffe1b646226eeeda5a4d4697c831e568ec1eec64, 3cd7db677a72521439b564dca7a4ca6d6c3a1c07, 719e3db9734708e812ceb33483ffaa2e064b4d59, 1cc7bbdef9330899a8207d3d55130f48321d68f1]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.4.0
|
||||
</details>
|
||||
|
||||
## 0.1.3-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [c7aff48a40a9a78924cbf27fd38930c536ee11e5, 5f92f9a27dca70d506d919351612bd32dc04241a, 13b4a7b2dc203959b77b3b0c5f154d3e34fe2058, 4b34bd62f2ac8d51efd2f48caea7092e87f30ce7, ffe1b646226eeeda5a4d4697c831e568ec1eec64, 3cd7db677a72521439b564dca7a4ca6d6c3a1c07, 719e3db9734708e812ceb33483ffaa2e064b4d59, 1cc7bbdef9330899a8207d3d55130f48321d68f1]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.4.0-rc.0
|
||||
</details>
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [73e12e1707baea845395e0582892f65456598672, 7380c44c751eff9ee624d80bf26370411ffed78b]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.3.1
|
||||
</details>
|
||||
|
||||
## 0.1.2-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [73e12e1707baea845395e0582892f65456598672, 7380c44c751eff9ee624d80bf26370411ffed78b]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.3.1-rc.0
|
||||
</details>
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [f7d47dd3517ec14ca2ec5c3c95fcdf9e1e2fb8b0, b6b04aadfcc8558f888b334e37c46a77e5816237, 4704bf81ca370f120af32185a7c55407a26f8514, 12897e25d0dc25b7373f5264d38f38a5a7444257, e45585b70a3a7b75434c88e4b2ea9af0a0764a76]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.3.0
|
||||
</details>
|
||||
|
||||
## 0.1.1-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [f7d47dd3517ec14ca2ec5c3c95fcdf9e1e2fb8b0, ae9f740d6af20557eac61b4af902c868b4132b49, b6b04aadfcc8558f888b334e37c46a77e5816237, 4704bf81ca370f120af32185a7c55407a26f8514, 12897e25d0dc25b7373f5264d38f38a5a7444257, e45585b70a3a7b75434c88e4b2ea9af0a0764a76]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.3.0-rc.0
|
||||
</details>
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- ([#39535](https://github.com/RocketChat/Rocket.Chat/pull/39535)) Adds externalIds field to livechat visitors for external platform identification.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ([#40175](https://github.com/RocketChat/Rocket.Chat/pull/40175)) Fixes an issue where the Omnichannel routing system ignored the `Livechat_accept_chats_with_no_agents` setting. Now, offline agents are correctly considered for assignment when the setting allows it.
|
||||
|
||||
- <details><summary>Updated dependencies [21cd54f87de5837fe6c999a44bd15be34b9fe905, 278b84f78360e53792a2e5d7620615039a0e15e9, 12c44d2db65af9f90c741da621164f5738fc19f5, 9713af36f5c0d673f2d2093015f322341706bab0, f3fa3ee2f2e53b777de9abd466f1b76a1ec1b96c, f4dfb8ddc2049692371aeb084110b5768151b5df]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.2.0
|
||||
</details>
|
||||
|
||||
## 0.1.0-rc.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.2.0-rc.2
|
||||
</details>
|
||||
|
||||
## 0.1.0-rc.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.2.0-rc.1
|
||||
</details>
|
||||
|
||||
## 0.0.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.4
|
||||
</details>
|
||||
|
||||
## 0.0.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.3
|
||||
</details>
|
||||
|
||||
## 0.1.0-rc.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- ([#39535](https://github.com/RocketChat/Rocket.Chat/pull/39535)) Adds externalIds field to livechat visitors for external platform identification.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ([#40175](https://github.com/RocketChat/Rocket.Chat/pull/40175)) Fixes an issue where the Omnichannel routing system ignored the `Livechat_accept_chats_with_no_agents` setting. Now, offline agents are correctly considered for assignment when the setting allows it.
|
||||
|
||||
- <details><summary>Updated dependencies [21cd54f87de5837fe6c999a44bd15be34b9fe905, 278b84f78360e53792a2e5d7620615039a0e15e9, 12c44d2db65af9f90c741da621164f5738fc19f5, 9713af36f5c0d673f2d2093015f322341706bab0, f3fa3ee2f2e53b777de9abd466f1b76a1ec1b96c, f4dfb8ddc2049692371aeb084110b5768151b5df]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.2.0-rc.0
|
||||
</details>
|
||||
|
||||
## 0.0.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ([#38989](https://github.com/RocketChat/Rocket.Chat/pull/38989)) chore(eslint): Upgrades ESLint and its configuration
|
||||
|
||||
- <details><summary>Updated dependencies [602b20a8c570b895eb296ecfe39c9b7fcb12fabd, e2068892bf1ffc88b15ab71ad743cf84e5d31ed5, cd2fc208d351032c0b729755af4886665dca08b6, 539659af22bc19880eda047dfc0b152472ccb65c, eae3fb3136bd0b48294c050a71b0a36d05ca02b0]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.2
|
||||
- @rocket.chat/patch-injection@0.0.2
|
||||
</details>
|
||||
|
||||
## 0.0.18-rc.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.2-rc.4
|
||||
</details>
|
||||
|
||||
## 0.0.18-rc.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.2-rc.3
|
||||
</details>
|
||||
|
||||
## 0.0.18-rc.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.2-rc.2
|
||||
</details>
|
||||
|
||||
## 0.0.18-rc.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.2-rc.1
|
||||
</details>
|
||||
|
||||
## 0.0.18-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ([#38989](https://github.com/RocketChat/Rocket.Chat/pull/38989)) chore(eslint): Upgrades ESLint and its configuration
|
||||
|
||||
- <details><summary>Updated dependencies [602b20a8c570b895eb296ecfe39c9b7fcb12fabd, e2068892bf1ffc88b15ab71ad743cf84e5d31ed5, cd2fc208d351032c0b729755af4886665dca08b6, 539659af22bc19880eda047dfc0b152472ccb65c, eae3fb3136bd0b48294c050a71b0a36d05ca02b0]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.2-rc.0
|
||||
- @rocket.chat/patch-injection@0.0.2-rc.0
|
||||
</details>
|
||||
|
||||
## 0.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.1
|
||||
</details>
|
||||
|
||||
## 0.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [d3758a7d57ab602745369ef9d2ccdbf9271cf305, e57f15845e4df048dd2f08f11aa08215780a2c34, 3b003e6b69c11b280d55bcc8db2f3e4ae7a4a573, 508b4a17d76dc1cd7d3a55bdba826216f51432e2, 123aebec2caa74b17d2b5dcbd2a2db2e687cf3ac]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.0
|
||||
</details>
|
||||
|
||||
## 0.0.16-rc.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.0-rc.2
|
||||
</details>
|
||||
|
||||
## 0.0.16-rc.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.0-rc.1
|
||||
</details>
|
||||
|
||||
## 0.0.16-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [d3758a7d57ab602745369ef9d2ccdbf9271cf305, e57f15845e4df048dd2f08f11aa08215780a2c34, 3b003e6b69c11b280d55bcc8db2f3e4ae7a4a573, 508b4a17d76dc1cd7d3a55bdba826216f51432e2, 123aebec2caa74b17d2b5dcbd2a2db2e687cf3ac]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.1.0-rc.0
|
||||
</details>
|
||||
|
||||
## 0.0.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.3
|
||||
</details>
|
||||
|
||||
## 0.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [f4fa393fccb6abefbe6fb6550563e8ca21fc792d, 020dfbcab6a940ca2e03d802d0f6b54714a34aa2, 5fa150953b86ff36face25083ed49e8c97a8044d, 05c415b94cb91907de39a39c6d277579258f334e]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.2
|
||||
</details>
|
||||
|
||||
## 0.0.14-rc.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.2-rc.2
|
||||
</details>
|
||||
|
||||
## 0.0.14-rc.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.1-rc.1
|
||||
</details>
|
||||
|
||||
## 0.0.14-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [f4fa393fccb6abefbe6fb6550563e8ca21fc792d, 020dfbcab6a940ca2e03d802d0f6b54714a34aa2, 5fa150953b86ff36face25083ed49e8c97a8044d, 05c415b94cb91907de39a39c6d277579258f334e]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.1-rc.0
|
||||
|
||||
</details>
|
||||
|
||||
## 0.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
- @rocket.chat/models@2.0.1
|
||||
|
||||
</details>
|
||||
|
||||
## 0.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [ac11ea05ffadeca978c794ff38d5199d9acb2c29, ac11ea05ffadeca978c794ff38d5199d9acb2c29, ddc935727e9a7275813006d9dcaa7fe866610844, 73d9eb2783176954f42aa2cbeda8abf1d49ac260, be80b724a636877294b5e5baa501d070941131dd]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.0
|
||||
</details>
|
||||
|
||||
## 0.0.12-rc.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.0-rc.5
|
||||
</details>
|
||||
|
||||
## 0.0.12-rc.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.0-rc.4
|
||||
</details>
|
||||
|
||||
## 0.0.12-rc.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.0-rc.3
|
||||
</details>
|
||||
|
||||
## 0.0.12-rc.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.0-rc.2
|
||||
</details>
|
||||
|
||||
## 0.0.12-rc.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.0-rc.1
|
||||
</details>
|
||||
|
||||
## 0.0.12-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [ac11ea05ffadeca978c794ff38d5199d9acb2c29, ac11ea05ffadeca978c794ff38d5199d9acb2c29, ddc935727e9a7275813006d9dcaa7fe866610844, 73d9eb2783176954f42aa2cbeda8abf1d49ac260, be80b724a636877294b5e5baa501d070941131dd]:</summary>
|
||||
|
||||
- @rocket.chat/models@2.0.0-rc.0
|
||||
</details>
|
||||
|
||||
## 0.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.8.2
|
||||
</details>
|
||||
|
||||
## 0.0.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.8.1
|
||||
</details>
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [83642cbe499ba399329449fb8cd652a0405c0795, 7f1b834a55b1240c226afde77713262da47f45dc]:</summary>
|
||||
|
||||
- @rocket.chat/models@1.8.0
|
||||
</details>
|
||||
|
||||
## 0.0.9-rc.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.8.0-rc.2
|
||||
</details>
|
||||
|
||||
## 0.0.9-rc.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
- @rocket.chat/models@1.8.0-rc.1
|
||||
</details>
|
||||
|
||||
## 0.0.9-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [83642cbe499ba399329449fb8cd652a0405c0795, 7f1b834a55b1240c226afde77713262da47f45dc]:</summary>
|
||||
|
||||
- @rocket.chat/models@1.8.0-rc.0
|
||||
</details>
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.3
|
||||
</details>
|
||||
|
||||
## 0.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [8cbd9bfd0566cbd7d86d8b40ea7d58d6ef382742]:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.2
|
||||
</details>
|
||||
|
||||
## 0.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.1
|
||||
</details>
|
||||
|
||||
## 0.0.6-rc.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.1-rc.4
|
||||
</details>
|
||||
|
||||
## 0.0.6-rc.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.1-rc.3
|
||||
</details>
|
||||
|
||||
## 0.0.6-rc.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.1-rc.2
|
||||
</details>
|
||||
|
||||
## 0.0.6-rc.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.1-rc.1
|
||||
</details>
|
||||
|
||||
## 0.0.6-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.1-rc.0
|
||||
</details>
|
||||
|
||||
## 0.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [c102712222cc18c050f45f9279f8eafcff3e7e8a, 3e177dbd0b65d70bce8587287ba73b30170eb0f7]:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.0
|
||||
</details>
|
||||
|
||||
## 0.0.5-rc.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.0-rc.7
|
||||
</details>
|
||||
|
||||
## 0.0.5-rc.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.0-rc.6
|
||||
</details>
|
||||
|
||||
## 0.0.4-rc.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.0-rc.5
|
||||
</details>
|
||||
|
||||
## 0.0.4-rc.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.0-rc.4
|
||||
</details>
|
||||
|
||||
## 0.0.4-rc.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.0-rc.3
|
||||
</details>
|
||||
|
||||
## 0.0.4-rc.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
- @rocket.chat/models@1.7.0-rc.2
|
||||
</details>
|
||||
|
||||
## 0.0.4-rc.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.0-rc.1
|
||||
</details>
|
||||
|
||||
## 0.0.4-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [c102712222cc18c050f45f9279f8eafcff3e7e8a, 3e177dbd0b65d70bce8587287ba73b30170eb0f7]:</summary>
|
||||
|
||||
- @rocket.chat/models@1.7.0-rc.0
|
||||
</details>
|
||||
|
||||
## 0.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.6.2
|
||||
</details>
|
||||
|
||||
## 0.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
- @rocket.chat/models@1.6.1
|
||||
</details>
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [b25f05acd07762387fa45d67a1241b982c192f5d, fd32867fd4949bc2951a22075498ccb551cc6bbc, c7db598e9f3c2ad47f6a6be2a9ba7078533c245b]:</summary>
|
||||
|
||||
- @rocket.chat/models@1.6.0
|
||||
</details>
|
||||
|
||||
## 0.0.2-rc.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.6.0-rc.6
|
||||
</details>
|
||||
|
||||
## 0.0.2-rc.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.6.0-rc.5
|
||||
</details>
|
||||
|
||||
## 0.0.2-rc.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.6.0-rc.4
|
||||
</details>
|
||||
|
||||
## 0.0.2-rc.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.6.0-rc.3
|
||||
</details>
|
||||
|
||||
## 0.0.2-rc.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.6.0-rc.2
|
||||
</details>
|
||||
|
||||
## 0.0.2-rc.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies []:</summary>
|
||||
|
||||
- @rocket.chat/models@1.6.0-rc.1
|
||||
</details>
|
||||
|
||||
## 0.0.2-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- <details><summary>Updated dependencies [b25f05acd07762387fa45d67a1241b982c192f5d, fd32867fd4949bc2951a22075498ccb551cc6bbc, c7db598e9f3c2ad47f6a6be2a9ba7078533c245b]:</summary>
|
||||
|
||||
- @rocket.chat/models@1.6.0-rc.0
|
||||
</details>
|
||||
6
packages/omni-core/jest.config.ts
Normal file
6
packages/omni-core/jest.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import server from '@rocket.chat/jest-presets/server';
|
||||
import type { Config } from 'jest';
|
||||
|
||||
export default {
|
||||
preset: server.preset,
|
||||
} satisfies Config;
|
||||
36
packages/omni-core/package.json
Normal file
36
packages/omni-core/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@rocket.chat/omni-core",
|
||||
"version": "0.1.4",
|
||||
"private": true,
|
||||
"main": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"/dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rm -rf dist && tsc -p tsconfig.json",
|
||||
"dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"test": "jest",
|
||||
"testunit": "jest",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rocket.chat/models": "workspace:^",
|
||||
"@rocket.chat/patch-injection": "workspace:^",
|
||||
"mongodb": "6.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rocket.chat/jest-presets": "workspace:~",
|
||||
"@rocket.chat/tools": "workspace:*",
|
||||
"@rocket.chat/tsconfig": "workspace:*",
|
||||
"@types/jest": "~30.0.0",
|
||||
"eslint": "~9.39.5",
|
||||
"jest": "~30.2.0",
|
||||
"typescript": "~5.9.3"
|
||||
},
|
||||
"volta": {
|
||||
"extends": "../../package.json"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { ILivechatDepartment } from '@rocket.chat/core-typings';
|
||||
import { makeFunction } from '@rocket.chat/patch-injection';
|
||||
import type { FilterOperators } from 'mongodb';
|
||||
|
||||
export const applyDepartmentRestrictions = makeFunction(async (query: FilterOperators<ILivechatDepartment> = {}, _userId: string) => {
|
||||
return query;
|
||||
});
|
||||
3
packages/omni-core/src/index.ts
Normal file
3
packages/omni-core/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './isDepartmentCreationAvailable';
|
||||
export * from './hooks/applyDepartmentRestrictions';
|
||||
export * from './visitor/create';
|
||||
20
packages/omni-core/src/isDepartmentCreationAvailable.spec.ts
Normal file
20
packages/omni-core/src/isDepartmentCreationAvailable.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { ILivechatDepartmentModel } from '@rocket.chat/model-typings';
|
||||
import { registerModel } from '@rocket.chat/models';
|
||||
|
||||
import { isDepartmentCreationAvailable } from './isDepartmentCreationAvailable';
|
||||
|
||||
it('should be available if there are no existing departments', async () => {
|
||||
registerModel('ILivechatDepartmentModel', { countTotal: () => 0 } as unknown as ILivechatDepartmentModel);
|
||||
|
||||
const isAvailable = await isDepartmentCreationAvailable();
|
||||
|
||||
expect(isAvailable).toBe(true);
|
||||
});
|
||||
|
||||
it('should not be available if there are existing departments', async () => {
|
||||
registerModel('ILivechatDepartmentModel', { countTotal: () => 1 } as unknown as ILivechatDepartmentModel);
|
||||
|
||||
const isAvailable = await isDepartmentCreationAvailable();
|
||||
|
||||
expect(isAvailable).toBe(false);
|
||||
});
|
||||
7
packages/omni-core/src/isDepartmentCreationAvailable.ts
Normal file
7
packages/omni-core/src/isDepartmentCreationAvailable.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { LivechatDepartment } from '@rocket.chat/models';
|
||||
import { makeFunction } from '@rocket.chat/patch-injection';
|
||||
|
||||
export const isDepartmentCreationAvailable = makeFunction(async (): Promise<boolean> => {
|
||||
// Only one department can exist at a time
|
||||
return (await LivechatDepartment.countTotal()) === 0;
|
||||
});
|
||||
779
packages/omni-core/src/visitor/create.spec.ts
Normal file
779
packages/omni-core/src/visitor/create.spec.ts
Normal file
@@ -0,0 +1,779 @@
|
||||
import { UserStatus } from '@rocket.chat/core-typings';
|
||||
import type { ILivechatContactsModel, ILivechatDepartmentModel, ILivechatVisitorsModel, IUsersModel } from '@rocket.chat/model-typings';
|
||||
import { registerModel } from '@rocket.chat/models';
|
||||
import { validateEmail } from '@rocket.chat/tools';
|
||||
|
||||
import { registerGuest } from './create';
|
||||
|
||||
// Mock the validateEmail function
|
||||
jest.mock('@rocket.chat/tools', () => ({
|
||||
validateEmail: jest.fn(),
|
||||
}));
|
||||
|
||||
// Mock the Logger
|
||||
jest.mock('@rocket.chat/logger', () => ({
|
||||
Logger: jest.fn().mockImplementation(() => ({
|
||||
debug: jest.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
const mockValidateEmail = validateEmail as jest.MockedFunction<typeof validateEmail>;
|
||||
|
||||
describe('registerGuest', () => {
|
||||
let updateOneByIdOrTokenSpy: jest.Mock;
|
||||
let getVisitorByTokenSpy: jest.Mock;
|
||||
let findOneVisitorByPhoneSpy: jest.Mock;
|
||||
let findOneGuestByEmailAddressSpy: jest.Mock;
|
||||
let getNextVisitorUsernameSpy: jest.Mock;
|
||||
let findContactByEmailAndContactManagerSpy: jest.Mock;
|
||||
let findOneOnlineAgentByIdSpy: jest.Mock;
|
||||
let findOneByIdOrNameSpy: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockValidateEmail.mockImplementation(() => true);
|
||||
|
||||
// Create spies that return reasonable defaults
|
||||
updateOneByIdOrTokenSpy = jest.fn().mockResolvedValue({ _id: 'visitor-123', token: 'test-token' });
|
||||
getVisitorByTokenSpy = jest.fn().mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy = jest.fn().mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy = jest.fn().mockResolvedValue(null);
|
||||
getNextVisitorUsernameSpy = jest.fn().mockResolvedValue('guest-123');
|
||||
findContactByEmailAndContactManagerSpy = jest.fn().mockResolvedValue(null);
|
||||
findOneOnlineAgentByIdSpy = jest.fn().mockResolvedValue(null);
|
||||
findOneByIdOrNameSpy = jest.fn().mockResolvedValue(null);
|
||||
|
||||
// Register the models with spy functions
|
||||
registerModel('ILivechatVisitorsModel', {
|
||||
getVisitorByToken: getVisitorByTokenSpy,
|
||||
findOneVisitorByPhone: findOneVisitorByPhoneSpy,
|
||||
findOneGuestByEmailAddress: findOneGuestByEmailAddressSpy,
|
||||
getNextVisitorUsername: getNextVisitorUsernameSpy,
|
||||
updateOneByIdOrToken: updateOneByIdOrTokenSpy,
|
||||
} as unknown as ILivechatVisitorsModel);
|
||||
|
||||
registerModel('ILivechatContactsModel', {
|
||||
findContactByEmailAndContactManager: findContactByEmailAndContactManagerSpy,
|
||||
} as unknown as ILivechatContactsModel);
|
||||
|
||||
registerModel('IUsersModel', {
|
||||
findOneOnlineAgentById: findOneOnlineAgentByIdSpy,
|
||||
} as unknown as IUsersModel);
|
||||
|
||||
registerModel('ILivechatDepartmentModel', {
|
||||
findOneByIdOrName: findOneByIdOrNameSpy,
|
||||
} as unknown as ILivechatDepartmentModel);
|
||||
});
|
||||
|
||||
describe('validation', () => {
|
||||
it('should throw error when token is not provided', async () => {
|
||||
const guestData = {};
|
||||
|
||||
await expect(registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false })).rejects.toThrow(
|
||||
'error-invalid-token',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error when token is empty string', async () => {
|
||||
const guestData = {
|
||||
token: '',
|
||||
};
|
||||
|
||||
await expect(registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false })).rejects.toThrow(
|
||||
'error-invalid-token',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('email validation and contact manager assignment', () => {
|
||||
it('should validate email and assign contact manager when available', async () => {
|
||||
const email = 'test@example.com';
|
||||
const token = 'test-token';
|
||||
const agentId = 'agent-123';
|
||||
|
||||
const mockAgent = {
|
||||
_id: agentId,
|
||||
username: 'agent.user',
|
||||
name: 'Agent User',
|
||||
emails: [{ address: 'agent@example.com' }],
|
||||
};
|
||||
|
||||
const mockContact = {
|
||||
contactManager: agentId,
|
||||
};
|
||||
|
||||
findContactByEmailAndContactManagerSpy.mockResolvedValue(mockContact);
|
||||
findOneOnlineAgentByIdSpy.mockResolvedValue(mockAgent);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
email,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(mockValidateEmail).toHaveBeenCalledWith('test@example.com');
|
||||
expect(findContactByEmailAndContactManagerSpy).toHaveBeenCalledWith('test@example.com');
|
||||
expect(findOneOnlineAgentByIdSpy).toHaveBeenCalledWith(agentId, false, false, {
|
||||
projection: { _id: 1, username: 1, name: 1, emails: 1 },
|
||||
});
|
||||
|
||||
// Verify the data passed to updateOneByIdOrToken
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
status: UserStatus.ONLINE,
|
||||
visitorEmails: [{ address: email }],
|
||||
contactManager: {
|
||||
_id: agentId,
|
||||
username: 'agent.user',
|
||||
name: 'Agent User',
|
||||
emails: [{ address: 'agent@example.com' }],
|
||||
},
|
||||
username: 'guest-123',
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should not assign contact manager when agent is not found', async () => {
|
||||
const email = 'test@example.com';
|
||||
const token = 'test-token';
|
||||
|
||||
const mockContact = {
|
||||
contactManager: 'agent-123',
|
||||
};
|
||||
|
||||
findContactByEmailAndContactManagerSpy.mockResolvedValue(mockContact);
|
||||
findOneOnlineAgentByIdSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
email,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
// Verify contact manager is not included in the data
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
status: UserStatus.ONLINE,
|
||||
visitorEmails: [{ address: email }],
|
||||
username: 'guest-123',
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
|
||||
// Ensure contactManager is not present
|
||||
const callArgs = updateOneByIdOrTokenSpy.mock.calls[0][0];
|
||||
expect(callArgs.contactManager).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should trim and lowercase email', async () => {
|
||||
const email = ' TEST@EXAMPLE.COM ';
|
||||
const token = 'test-token';
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
email,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(mockValidateEmail).toHaveBeenCalledWith('test@example.com');
|
||||
expect(findContactByEmailAndContactManagerSpy).toHaveBeenCalledWith('test@example.com');
|
||||
|
||||
// Verify the trimmed and lowercase email is passed to updateOneByIdOrToken
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
visitorEmails: [{ address: 'test@example.com' }],
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('department validation and assignment', () => {
|
||||
it('should assign valid department', async () => {
|
||||
const token = 'test-token';
|
||||
const department = 'sales';
|
||||
const departmentId = 'dept-123';
|
||||
|
||||
const mockDepartment = {
|
||||
_id: departmentId,
|
||||
};
|
||||
|
||||
findOneByIdOrNameSpy.mockResolvedValue(mockDepartment);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
department,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(findOneByIdOrNameSpy).toHaveBeenCalledWith(department, { projection: { _id: 1 } });
|
||||
|
||||
// Verify the department ID is passed to updateOneByIdOrToken
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
status: UserStatus.ONLINE,
|
||||
department: departmentId,
|
||||
username: 'guest-123',
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error for invalid department', async () => {
|
||||
const token = 'test-token';
|
||||
const department = 'invalid-dept';
|
||||
|
||||
findOneByIdOrNameSpy.mockResolvedValue(null);
|
||||
getVisitorByTokenSpy.mockResolvedValue({ department: 'different-dept' });
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
department,
|
||||
};
|
||||
|
||||
await expect(registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false })).rejects.toThrow(
|
||||
'error-invalid-department',
|
||||
);
|
||||
|
||||
// Verify updateOneByIdOrToken is not called when department validation fails
|
||||
expect(updateOneByIdOrTokenSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not validate department if visitor already has the same department', async () => {
|
||||
const token = 'test-token';
|
||||
const department = 'sales';
|
||||
|
||||
getVisitorByTokenSpy.mockResolvedValue({
|
||||
_id: 'visitor-123',
|
||||
department,
|
||||
token,
|
||||
});
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
department,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
// Department validation should be skipped
|
||||
expect(findOneByIdOrNameSpy).not.toHaveBeenCalled();
|
||||
|
||||
// Verify existing visitor is updated without department validation
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
_id: 'visitor-123',
|
||||
token,
|
||||
status: UserStatus.ONLINE,
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('visitor matching and creation', () => {
|
||||
it('should update existing visitor found by token', async () => {
|
||||
const token = 'test-token';
|
||||
const existingVisitor = {
|
||||
_id: 'visitor-123',
|
||||
token,
|
||||
};
|
||||
|
||||
getVisitorByTokenSpy.mockResolvedValue(existingVisitor);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
name: 'Updated Name',
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(getVisitorByTokenSpy).toHaveBeenCalledWith(token, { projection: { _id: 1, department: 1, token: 1 } });
|
||||
|
||||
// Verify existing visitor data is used and updated
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
_id: 'visitor-123',
|
||||
token,
|
||||
status: UserStatus.ONLINE,
|
||||
name: 'Updated Name',
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should match visitor by phone number and preserve existing token', async () => {
|
||||
const token = 'new-token';
|
||||
const existingToken = 'existing-token';
|
||||
const phoneNumber = '+1234567890';
|
||||
const existingVisitor = {
|
||||
_id: 'visitor-123',
|
||||
token: existingToken,
|
||||
};
|
||||
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(existingVisitor);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
phone: { number: phoneNumber },
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(findOneVisitorByPhoneSpy).toHaveBeenCalledWith(phoneNumber);
|
||||
|
||||
// Verify existing visitor's token is preserved, not the new one
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
_id: 'visitor-123',
|
||||
token: existingToken, // Should use existing token, not new one
|
||||
status: UserStatus.ONLINE,
|
||||
phone: [{ phoneNumber }],
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should match visitor by email', async () => {
|
||||
const token = 'test-token';
|
||||
const email = 'test@example.com';
|
||||
const existingVisitor = {
|
||||
_id: 'visitor-123',
|
||||
token: 'existing-token',
|
||||
};
|
||||
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(existingVisitor);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
email,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(findOneGuestByEmailAddressSpy).toHaveBeenCalledWith(email);
|
||||
|
||||
// Verify existing visitor data is used
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
_id: 'visitor-123',
|
||||
token,
|
||||
status: UserStatus.ONLINE,
|
||||
visitorEmails: [{ address: email }],
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should create new visitor when no matches found', async () => {
|
||||
const token = 'test-token';
|
||||
const username = 'custom-username';
|
||||
const id = 'custom-id';
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
id,
|
||||
token,
|
||||
username,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
// Verify new visitor data is created with provided values
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
_id: id,
|
||||
token,
|
||||
username,
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should generate username when not provided for new visitor', async () => {
|
||||
const token = 'test-token';
|
||||
const generatedUsername = 'guest-123';
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(getNextVisitorUsernameSpy).toHaveBeenCalled();
|
||||
|
||||
// Verify generated username is used
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
username: generatedUsername,
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should use provided username for new visitor', async () => {
|
||||
const token = 'test-token';
|
||||
const providedUsername = 'custom-username';
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
username: providedUsername,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(getNextVisitorUsernameSpy).not.toHaveBeenCalled();
|
||||
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
username: providedUsername,
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('data formatting', () => {
|
||||
it('should format phone number correctly', async () => {
|
||||
const token = 'test-token';
|
||||
const phoneNumber = '+1234567890';
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
phone: { number: phoneNumber },
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
phone: [{ phoneNumber }],
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should format email correctly', async () => {
|
||||
const token = 'test-token';
|
||||
const email = 'test@example.com';
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
findContactByEmailAndContactManagerSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
email,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
visitorEmails: [{ address: email }],
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should use default status when not provided', async () => {
|
||||
const token = 'test-token';
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should use custom status when provided', async () => {
|
||||
const token = 'test-token';
|
||||
const status = UserStatus.AWAY;
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
status,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
status,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('connection data handling', () => {
|
||||
it('should save connection data for new visitor', async () => {
|
||||
const token = 'test-token';
|
||||
const connectionData = {
|
||||
httpHeaders: {
|
||||
'user-agent': 'Mozilla/5.0',
|
||||
'x-real-ip': '192.168.1.1',
|
||||
'host': 'example.com',
|
||||
},
|
||||
clientAddress: '10.0.0.1',
|
||||
};
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
connectionData,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
userAgent: 'Mozilla/5.0',
|
||||
ip: '192.168.1.1',
|
||||
host: 'example.com',
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should use x-forwarded-for header when x-real-ip is not available', async () => {
|
||||
const token = 'test-token';
|
||||
const connectionData = {
|
||||
httpHeaders: {
|
||||
'x-forwarded-for': '203.0.113.1',
|
||||
},
|
||||
clientAddress: '10.0.0.1',
|
||||
};
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
connectionData,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
ip: '203.0.113.1',
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should use clientAddress when no IP headers are available', async () => {
|
||||
const token = 'test-token';
|
||||
const connectionData = {
|
||||
httpHeaders: {},
|
||||
clientAddress: '10.0.0.1',
|
||||
};
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
connectionData,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
ip: '10.0.0.1',
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error scenarios', () => {
|
||||
it('should return null when upsert fails', async () => {
|
||||
const token = 'test-token';
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
|
||||
// Mock upsert to return null (failure case)
|
||||
updateOneByIdOrTokenSpy.mockResolvedValue(null);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
};
|
||||
|
||||
const result = await registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false });
|
||||
|
||||
expect(result).toBeNull();
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error when email validation fails', async () => {
|
||||
const token = 'test-token';
|
||||
const email = 'invalid-email';
|
||||
|
||||
mockValidateEmail.mockImplementation(() => {
|
||||
throw new Error('Invalid email');
|
||||
});
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
email,
|
||||
};
|
||||
|
||||
await expect(registerGuest(guestData, { shouldConsiderIdleAgent: false, shouldConsiderOfflineAgent: false })).rejects.toThrow(
|
||||
'Invalid email',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldConsiderIdleAgent parameter', () => {
|
||||
it('should pass shouldConsiderIdleAgent and shouldConsiderOfflineAgent to findOneOnlineAgentById', async () => {
|
||||
const token = 'test-token';
|
||||
const email = 'test@example.com';
|
||||
const agentId = 'agent-123';
|
||||
|
||||
const mockAgent = {
|
||||
_id: agentId,
|
||||
username: 'agent.user',
|
||||
name: 'Agent User',
|
||||
emails: [{ address: 'agent@example.com' }],
|
||||
};
|
||||
|
||||
const mockContact = {
|
||||
contactManager: agentId,
|
||||
};
|
||||
|
||||
// All lookup methods return null (no matches)
|
||||
getVisitorByTokenSpy.mockResolvedValue(null);
|
||||
findOneVisitorByPhoneSpy.mockResolvedValue(null);
|
||||
findOneGuestByEmailAddressSpy.mockResolvedValue(null);
|
||||
findContactByEmailAndContactManagerSpy.mockResolvedValue(mockContact);
|
||||
findOneOnlineAgentByIdSpy.mockResolvedValue(mockAgent);
|
||||
|
||||
const guestData = {
|
||||
token,
|
||||
email,
|
||||
};
|
||||
|
||||
await registerGuest(guestData, { shouldConsiderIdleAgent: true, shouldConsiderOfflineAgent: true });
|
||||
|
||||
expect(findOneOnlineAgentByIdSpy).toHaveBeenCalledWith(
|
||||
agentId,
|
||||
true, // shouldConsiderIdleAgent should be true
|
||||
true, // shouldConsiderOfflineAgent should be true
|
||||
{ projection: { _id: 1, username: 1, name: 1, emails: 1 } },
|
||||
);
|
||||
|
||||
expect(updateOneByIdOrTokenSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
token,
|
||||
visitorEmails: [{ address: email }],
|
||||
contactManager: expect.objectContaining({
|
||||
username: 'agent.user',
|
||||
}),
|
||||
status: UserStatus.ONLINE,
|
||||
ts: expect.any(Date),
|
||||
}),
|
||||
{ upsert: true, returnDocument: 'after' },
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
118
packages/omni-core/src/visitor/create.ts
Normal file
118
packages/omni-core/src/visitor/create.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { type ILivechatVisitor, type IVisitorExternalIdentifier, UserStatus } from '@rocket.chat/core-typings';
|
||||
import { Logger } from '@rocket.chat/logger';
|
||||
import { LivechatContacts, LivechatDepartment, LivechatVisitors, Users } from '@rocket.chat/models';
|
||||
import { makeFunction } from '@rocket.chat/patch-injection';
|
||||
import { validateEmail } from '@rocket.chat/tools';
|
||||
|
||||
const logger = new Logger('Livechat - Visitor');
|
||||
|
||||
type RegisterGuestType = Partial<Pick<ILivechatVisitor, 'token' | 'name' | 'department' | 'status' | 'username'>> & {
|
||||
id?: string;
|
||||
connectionData?: any;
|
||||
email?: string;
|
||||
phone?: { number: string };
|
||||
externalIds?: IVisitorExternalIdentifier[];
|
||||
};
|
||||
|
||||
export const registerGuest = makeFunction(
|
||||
async (
|
||||
{ id, token, name, phone, email, department, username, connectionData, status = UserStatus.ONLINE, externalIds }: RegisterGuestType,
|
||||
{ shouldConsiderIdleAgent, shouldConsiderOfflineAgent }: { shouldConsiderIdleAgent: boolean; shouldConsiderOfflineAgent: boolean },
|
||||
): Promise<ILivechatVisitor | null> => {
|
||||
if (!token) {
|
||||
throw Error('error-invalid-token');
|
||||
}
|
||||
|
||||
logger.debug({ msg: 'New incoming conversation', id, token });
|
||||
|
||||
const visitorDataToUpdate: Partial<ILivechatVisitor> & { userAgent?: string; ip?: string; host?: string } = {
|
||||
token,
|
||||
status,
|
||||
...(phone?.number && { phone: [{ phoneNumber: phone.number }] }),
|
||||
...(name && { name }),
|
||||
...(externalIds?.length && { externalIds }),
|
||||
};
|
||||
|
||||
if (email) {
|
||||
const visitorEmail = email.trim().toLowerCase();
|
||||
validateEmail(visitorEmail);
|
||||
visitorDataToUpdate.visitorEmails = [{ address: visitorEmail }];
|
||||
|
||||
const contact = await LivechatContacts.findContactByEmailAndContactManager(visitorEmail);
|
||||
if (contact?.contactManager) {
|
||||
const agent = await Users.findOneOnlineAgentById(contact.contactManager, shouldConsiderIdleAgent, shouldConsiderOfflineAgent, {
|
||||
projection: { _id: 1, username: 1, name: 1, emails: 1 },
|
||||
});
|
||||
if (agent?.username && agent.name && agent.emails) {
|
||||
visitorDataToUpdate.contactManager = {
|
||||
_id: agent._id,
|
||||
username: agent.username,
|
||||
name: agent.name,
|
||||
emails: agent.emails,
|
||||
};
|
||||
logger.debug({ msg: 'Assigning visitor to agent', token, agent: agent.username });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const livechatVisitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1, department: 1, token: 1 } });
|
||||
|
||||
if (department && livechatVisitor?.department !== department) {
|
||||
logger.debug({ msg: 'Attempt to find a department with id/name', department });
|
||||
const dep = await LivechatDepartment.findOneByIdOrName(department, { projection: { _id: 1 } });
|
||||
if (!dep) {
|
||||
logger.debug({ msg: 'Invalid department provided', department });
|
||||
// throw new Meteor.Error('error-invalid-department', 'The provided department is invalid');
|
||||
throw new Error('error-invalid-department');
|
||||
}
|
||||
logger.debug({ msg: 'Assigning visitor to department', token, department: dep._id });
|
||||
visitorDataToUpdate.department = dep._id;
|
||||
}
|
||||
|
||||
visitorDataToUpdate.token = livechatVisitor?.token || token;
|
||||
|
||||
let existingUser = null;
|
||||
|
||||
if (livechatVisitor) {
|
||||
logger.debug('Found matching user by token');
|
||||
visitorDataToUpdate._id = livechatVisitor._id;
|
||||
} else if (phone?.number && (existingUser = await LivechatVisitors.findOneVisitorByPhone(phone.number))) {
|
||||
logger.debug('Found matching user by phone number');
|
||||
visitorDataToUpdate._id = existingUser._id;
|
||||
// Don't change token when matching by phone number, use current visitor token
|
||||
visitorDataToUpdate.token = existingUser.token;
|
||||
} else if (email && (existingUser = await LivechatVisitors.findOneGuestByEmailAddress(email))) {
|
||||
logger.debug('Found matching user by email');
|
||||
visitorDataToUpdate._id = existingUser._id;
|
||||
} else if (!livechatVisitor) {
|
||||
logger.debug({ msg: 'No matches found. Attempting to create new user with token', token });
|
||||
|
||||
visitorDataToUpdate._id = id || undefined;
|
||||
visitorDataToUpdate.username = username || (await LivechatVisitors.getNextVisitorUsername());
|
||||
visitorDataToUpdate.status = status;
|
||||
visitorDataToUpdate.ts = new Date();
|
||||
|
||||
if (connectionData && typeof connectionData === 'object') {
|
||||
logger.debug({ msg: 'Saving connection data for visitor', token });
|
||||
const { httpHeaders, clientAddress } = connectionData;
|
||||
if (httpHeaders && typeof httpHeaders === 'object') {
|
||||
visitorDataToUpdate.userAgent = httpHeaders['user-agent'];
|
||||
visitorDataToUpdate.ip = httpHeaders['x-real-ip'] || httpHeaders['x-forwarded-for'] || clientAddress;
|
||||
visitorDataToUpdate.host = httpHeaders.host;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const upsertedLivechatVisitor = await LivechatVisitors.updateOneByIdOrToken(visitorDataToUpdate, {
|
||||
upsert: true,
|
||||
returnDocument: 'after',
|
||||
});
|
||||
|
||||
if (!upsertedLivechatVisitor) {
|
||||
logger.debug('No visitor found after upsert');
|
||||
return null;
|
||||
}
|
||||
|
||||
return upsertedLivechatVisitor;
|
||||
},
|
||||
);
|
||||
9
packages/omni-core/tsconfig.json
Normal file
9
packages/omni-core/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@rocket.chat/tsconfig/server.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"declaration": true
|
||||
},
|
||||
"include": ["./src/**/*"],
|
||||
}
|
||||
Reference in New Issue
Block a user