diff --git a/apps/web/public/static/images/providers/dark/rocket-chat.svg b/apps/web/public/static/images/providers/dark/rocket-chat.svg
new file mode 100644
index 00000000000..8c1fd745bb9
--- /dev/null
+++ b/apps/web/public/static/images/providers/dark/rocket-chat.svg
@@ -0,0 +1,6 @@
+
diff --git a/apps/web/public/static/images/providers/dark/square/rocket-chat.svg b/apps/web/public/static/images/providers/dark/square/rocket-chat.svg
new file mode 100644
index 00000000000..8c1fd745bb9
--- /dev/null
+++ b/apps/web/public/static/images/providers/dark/square/rocket-chat.svg
@@ -0,0 +1,6 @@
+
diff --git a/apps/web/public/static/images/providers/light/rocket-chat.svg b/apps/web/public/static/images/providers/light/rocket-chat.svg
new file mode 100644
index 00000000000..8c1fd745bb9
--- /dev/null
+++ b/apps/web/public/static/images/providers/light/rocket-chat.svg
@@ -0,0 +1,6 @@
+
diff --git a/apps/web/public/static/images/providers/light/square/rocket-chat.svg b/apps/web/public/static/images/providers/light/square/rocket-chat.svg
new file mode 100644
index 00000000000..8c1fd745bb9
--- /dev/null
+++ b/apps/web/public/static/images/providers/light/square/rocket-chat.svg
@@ -0,0 +1,6 @@
+
diff --git a/libs/shared/src/consts/providers/channels/chat.ts b/libs/shared/src/consts/providers/channels/chat.ts
index 5234c9c783b..9d354dd6536 100644
--- a/libs/shared/src/consts/providers/channels/chat.ts
+++ b/libs/shared/src/consts/providers/channels/chat.ts
@@ -1,5 +1,5 @@
import { IConfigCredentials, IProviderConfig } from '../provider.interface';
-import { grafanaOnCallConfig, slackConfig, getstreamConfig } from '../credentials';
+import { grafanaOnCallConfig, slackConfig, getstreamConfig, rocketChatConfig } from '../credentials';
import { ChatProviderIdEnum } from '../provider.enum';
@@ -70,4 +70,12 @@ export const chatProviders: IProviderConfig[] = [
docReference: 'https://getstream.io/chat/docs/node/?language=javascript',
logoFileName: { light: 'getstream.svg', dark: 'getstream.svg' },
},
+ {
+ id: ChatProviderIdEnum.RocketChat,
+ displayName: 'Rocket.Chat',
+ channel: ChannelTypeEnum.CHAT,
+ credentials: rocketChatConfig,
+ docReference: 'https://developer.rocket.chat/reference/api/rest-api/endpoints',
+ logoFileName: { light: 'rocket-chat.svg', dark: 'rocket-chat.svg' },
+ },
];
diff --git a/libs/shared/src/consts/providers/credentials/provider-credentials.ts b/libs/shared/src/consts/providers/credentials/provider-credentials.ts
index 3b5a4a9cdb4..0ef8ac4673b 100644
--- a/libs/shared/src/consts/providers/credentials/provider-credentials.ts
+++ b/libs/shared/src/consts/providers/credentials/provider-credentials.ts
@@ -1046,3 +1046,20 @@ export const azureSmsConfig: IConfigCredentials[] = [
},
...smsConfigBase,
];
+
+export const rocketChatConfig: IConfigCredentials[] = [
+ {
+ key: CredentialsKeyEnum.Token,
+ displayName: 'Personal Access Token (x-auth-token)',
+ description: 'Personal Access Token of your user',
+ type: 'text',
+ required: true,
+ },
+ {
+ key: CredentialsKeyEnum.User,
+ displayName: 'User id (x-user-id)',
+ description: 'Your User id',
+ type: 'text',
+ required: true,
+ },
+];
diff --git a/libs/shared/src/consts/providers/provider.enum.ts b/libs/shared/src/consts/providers/provider.enum.ts
index edad4da36e3..a83d61911ab 100644
--- a/libs/shared/src/consts/providers/provider.enum.ts
+++ b/libs/shared/src/consts/providers/provider.enum.ts
@@ -109,6 +109,7 @@ export enum ChatProviderIdEnum {
Zulip = 'zulip',
GrafanaOnCall = 'grafana-on-call',
GetStream = 'getstream',
+ RocketChat = 'rocket-chat',
}
export enum PushProviderIdEnum {
diff --git a/packages/application-generic/package.json b/packages/application-generic/package.json
index c276de0a0bc..a185a4eec5c 100644
--- a/packages/application-generic/package.json
+++ b/packages/application-generic/package.json
@@ -114,6 +114,7 @@
"@novu/twilio": "^0.22.0",
"@novu/zulip": "^0.22.0",
"@novu/nexmo": "^0.22.0",
+ "@novu/rocket-chat": "^0.22.0",
"@sentry/node": "^7.12.1",
"@segment/analytics-node": "^1.1.4",
"axios": "^1.6.2",
diff --git a/packages/application-generic/src/factories/chat/chat.factory.ts b/packages/application-generic/src/factories/chat/chat.factory.ts
index 957afccada3..ca365353e51 100644
--- a/packages/application-generic/src/factories/chat/chat.factory.ts
+++ b/packages/application-generic/src/factories/chat/chat.factory.ts
@@ -8,6 +8,7 @@ import { GrafanaOnCallHandler } from './handlers/grafana-on-call.handler';
import { RyverHandler } from './handlers/ryver.handler';
import { ZulipHandler } from './handlers/zulip.handler';
import { GetstreamChatHandler } from './handlers/getstream.handler';
+import { RocketChatHandler } from './handlers/rocket-chat.handler';
export class ChatFactory implements IChatFactory {
handlers: IChatHandler[] = [
@@ -19,6 +20,7 @@ export class ChatFactory implements IChatFactory {
new ZulipHandler(),
new GrafanaOnCallHandler(),
new GetstreamChatHandler(),
+ new RocketChatHandler(),
];
getHandler(integration: IntegrationEntity) {
diff --git a/packages/application-generic/src/factories/chat/handlers/rocket-chat.handler.ts b/packages/application-generic/src/factories/chat/handlers/rocket-chat.handler.ts
new file mode 100644
index 00000000000..e31b47aee5b
--- /dev/null
+++ b/packages/application-generic/src/factories/chat/handlers/rocket-chat.handler.ts
@@ -0,0 +1,18 @@
+import { ICredentials, ChatProviderIdEnum } from '@novu/shared';
+import { ChannelTypeEnum } from '@novu/stateless';
+import { BaseChatHandler } from './base.handler';
+import { RocketChatProvider } from '@novu/rocket-chat';
+
+export class RocketChatHandler extends BaseChatHandler {
+ constructor() {
+ super(ChatProviderIdEnum.RocketChat, ChannelTypeEnum.CHAT);
+ }
+
+ buildProvider(credentials: ICredentials) {
+ const config: { token: string; user: string } = {
+ token: credentials.token as string,
+ user: credentials.user as string,
+ };
+ this.provider = new RocketChatProvider(config);
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fbd9d0a143f..fe597dd94a8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2600,6 +2600,9 @@ importers:
'@novu/resend':
specifier: ^0.22.0
version: link:../../providers/resend
+ '@novu/rocket-chat':
+ specifier: ^0.22.0
+ version: link:../../providers/rocket-chat
'@novu/ryver':
specifier: ^0.22.0
version: link:../../providers/ryver
@@ -5671,6 +5674,49 @@ importers:
specifier: 4.9.5
version: 4.9.5
+ providers/rocket-chat:
+ dependencies:
+ '@novu/stateless':
+ specifier: 0.16.3
+ version: 0.16.3
+ axios:
+ specifier: ^1.6.2
+ version: 1.6.2
+ devDependencies:
+ '@istanbuljs/nyc-config-typescript':
+ specifier: ~1.0.1
+ version: 1.0.2(nyc@15.1.0)
+ '@types/jest':
+ specifier: ~27.5.2
+ version: 27.5.2
+ cspell:
+ specifier: ~6.19.2
+ version: 6.19.2
+ jest:
+ specifier: ~27.5.1
+ version: 27.5.1(ts-node@10.9.1)
+ npm-run-all:
+ specifier: ^4.1.5
+ version: 4.1.5
+ nyc:
+ specifier: ~15.1.0
+ version: 15.1.0
+ prettier:
+ specifier: ~2.8.0
+ version: 2.8.7
+ rimraf:
+ specifier: ~3.0.2
+ version: 3.0.2
+ ts-jest:
+ specifier: ~27.1.5
+ version: 27.1.5(@babel/core@7.23.2)(@types/jest@27.5.2)(jest@27.5.1)(typescript@4.9.5)
+ ts-node:
+ specifier: ~10.9.1
+ version: 10.9.1(@types/node@14.18.42)(typescript@4.9.5)
+ typescript:
+ specifier: 4.9.5
+ version: 4.9.5
+
providers/ryver:
dependencies:
'@novu/stateless':
@@ -17100,6 +17146,15 @@ packages:
vue-resize: 2.0.0-alpha.1(vue@3.2.47)
dev: false
+ /@novu/stateless@0.16.3:
+ resolution: {integrity: sha512-9wrqUluSaR9rCdDp4oUWFBwsk6OSM4P+yMaEtFGHrGmSPMcspnvXQaQoARKakuUpCXFvQlPPJq5+yaicv4o/hA==}
+ engines: {node: '>=10'}
+ dependencies:
+ handlebars: 4.7.7
+ lodash.get: 4.4.2
+ lodash.merge: 4.6.2
+ dev: false
+
/@npmcli/arborist@5.3.0:
resolution: {integrity: sha512-+rZ9zgL1lnbl8Xbb1NQdMjveOMwj4lIYfcDtyJHHi5x4X8jtR6m8SXooJMZy5vmFVZ8w7A2Bnd/oX9eTuU8w5A==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@@ -49640,13 +49695,13 @@ packages:
uglify-js:
optional: true
dependencies:
- '@jridgewell/trace-mapping': 0.3.19
+ '@jridgewell/trace-mapping': 0.3.18
'@swc/core': 1.3.49
esbuild: 0.18.20
jest-worker: 27.5.1
- schema-utils: 3.3.0
+ schema-utils: 3.1.2
serialize-javascript: 6.0.1
- terser: 5.22.0
+ terser: 5.16.9
webpack: 5.78.0(@swc/core@1.3.49)(esbuild@0.18.20)(webpack-cli@5.1.4)
/terser-webpack-plugin@5.3.9(@swc/core@1.3.49)(esbuild@0.18.20)(webpack@5.82.1):
@@ -49690,12 +49745,12 @@ packages:
uglify-js:
optional: true
dependencies:
- '@jridgewell/trace-mapping': 0.3.19
+ '@jridgewell/trace-mapping': 0.3.18
esbuild: 0.18.17
jest-worker: 27.5.1
- schema-utils: 3.3.0
+ schema-utils: 3.1.2
serialize-javascript: 6.0.1
- terser: 5.22.0
+ terser: 5.16.9
webpack: 5.88.2(esbuild@0.18.17)
dev: true
@@ -49715,11 +49770,11 @@ packages:
uglify-js:
optional: true
dependencies:
- '@jridgewell/trace-mapping': 0.3.19
+ '@jridgewell/trace-mapping': 0.3.18
jest-worker: 27.5.1
- schema-utils: 3.3.0
+ schema-utils: 3.1.2
serialize-javascript: 6.0.1
- terser: 5.22.0
+ terser: 5.16.9
webpack: 5.78.0
dev: true
diff --git a/providers/rocket-chat/.czrc b/providers/rocket-chat/.czrc
new file mode 100644
index 00000000000..d1bcc209ca1
--- /dev/null
+++ b/providers/rocket-chat/.czrc
@@ -0,0 +1,3 @@
+{
+ "path": "cz-conventional-changelog"
+}
diff --git a/providers/rocket-chat/.eslintrc.json b/providers/rocket-chat/.eslintrc.json
new file mode 100644
index 00000000000..ec40100be69
--- /dev/null
+++ b/providers/rocket-chat/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../../.eslintrc.js"
+}
diff --git a/providers/rocket-chat/.gitignore b/providers/rocket-chat/.gitignore
new file mode 100644
index 00000000000..963d5292865
--- /dev/null
+++ b/providers/rocket-chat/.gitignore
@@ -0,0 +1,9 @@
+.idea/*
+.nyc_output
+build
+node_modules
+test
+src/**.js
+coverage
+*.log
+package-lock.json
diff --git a/providers/rocket-chat/README.md b/providers/rocket-chat/README.md
new file mode 100644
index 00000000000..ea2bbe8587b
--- /dev/null
+++ b/providers/rocket-chat/README.md
@@ -0,0 +1,14 @@
+# Novu RocketChat Provider
+
+A RocketChat chat provider library for [@novu/node](https://github.com/novuhq/novu)
+
+## Usage
+
+```javascript
+import { RocketChatProvider } from '@novu/rocket-chat';
+
+const provider = new RocketChatProvider({
+ user: process.env.ROCKET_CHAT_USER_ID,
+ token: process.env.ROCKET_CHAT_TOKEN,
+});
+```
diff --git a/providers/rocket-chat/jest.config.js b/providers/rocket-chat/jest.config.js
new file mode 100644
index 00000000000..61faa20934a
--- /dev/null
+++ b/providers/rocket-chat/jest.config.js
@@ -0,0 +1,8 @@
+/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
+module.exports = {
+ preset: 'ts-jest',
+ testEnvironment: 'node',
+ moduleNameMapper: {
+ axios: 'axios/dist/node/axios.cjs',
+ },
+};
diff --git a/providers/rocket-chat/package.json b/providers/rocket-chat/package.json
new file mode 100644
index 00000000000..7d2b8fab745
--- /dev/null
+++ b/providers/rocket-chat/package.json
@@ -0,0 +1,78 @@
+{
+ "name": "@novu/rocket-chat",
+ "version": "0.22.0",
+ "description": "A rocket-chat wrapper for novu",
+ "main": "build/main/index.js",
+ "typings": "build/main/index.d.ts",
+ "module": "build/module/index.js",
+ "private": false,
+ "repository": "https://github.com/novuhq/novu",
+ "license": "MIT",
+ "keywords": [],
+ "scripts": {
+ "prebuild": "rimraf build",
+ "build": "run-p build:*",
+ "build:main": "tsc -p tsconfig.json",
+ "build:module": "tsc -p tsconfig.module.json",
+ "fix": "run-s fix:*",
+ "fix:prettier": "prettier \"src/**/*.ts\" --write",
+ "fix:lint": "eslint src --ext .ts --fix",
+ "test": "run-s test:*",
+ "lint": "eslint src --ext .ts",
+ "test:unit": "jest src",
+ "watch:build": "tsc -p tsconfig.json -w",
+ "watch:test": "jest src --watch",
+ "reset-hard": "git clean -dfx && git reset --hard && yarn",
+ "prepare-release": "run-s reset-hard test"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "dependencies": {
+ "@novu/stateless": "0.16.3",
+ "axios": "^1.6.2"
+ },
+ "devDependencies": {
+ "@istanbuljs/nyc-config-typescript": "~1.0.1",
+ "@types/jest": "~27.5.2",
+ "cspell": "~6.19.2",
+ "jest": "~27.5.1",
+ "npm-run-all": "^4.1.5",
+ "nyc": "~15.1.0",
+ "prettier": "~2.8.0",
+ "rimraf": "~3.0.2",
+ "ts-jest": "~27.1.5",
+ "ts-node": "~10.9.1",
+ "typescript": "4.9.5"
+ },
+ "files": [
+ "build/main",
+ "build/module",
+ "!**/*.spec.*",
+ "!**/*.json",
+ "CHANGELOG.md",
+ "LICENSE",
+ "README.md"
+ ],
+ "ava": {
+ "failFast": true,
+ "timeout": "60s",
+ "typescript": {
+ "rewritePaths": {
+ "src/": "build/main/"
+ }
+ },
+ "files": [
+ "!build/module/**"
+ ]
+ },
+ "prettier": {
+ "singleQuote": true
+ },
+ "nyc": {
+ "extends": "@istanbuljs/nyc-config-typescript",
+ "exclude": [
+ "**/*.spec.js"
+ ]
+ }
+}
diff --git a/providers/rocket-chat/src/index.ts b/providers/rocket-chat/src/index.ts
new file mode 100644
index 00000000000..65539af5399
--- /dev/null
+++ b/providers/rocket-chat/src/index.ts
@@ -0,0 +1 @@
+export * from './lib/rocket-chat.provider';
diff --git a/providers/rocket-chat/src/lib/rocket-chat.provider.spec.ts b/providers/rocket-chat/src/lib/rocket-chat.provider.spec.ts
new file mode 100644
index 00000000000..d861f99f532
--- /dev/null
+++ b/providers/rocket-chat/src/lib/rocket-chat.provider.spec.ts
@@ -0,0 +1,29 @@
+import { RocketChatProvider } from './rocket-chat.provider';
+
+test('should trigger rocket-chat library correctly', async () => {
+ const mockConfig = {
+ user: '',
+ token: '',
+ };
+ const provider = new RocketChatProvider(mockConfig);
+
+ const spy = jest
+ .spyOn(provider, 'sendMessage')
+ .mockImplementation(async () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return {} as any;
+ });
+
+ await provider.sendMessage({
+ webhookUrl: '',
+ channel: '',
+ content: '',
+ });
+
+ expect(spy).toHaveBeenCalled();
+ expect(spy).toHaveBeenCalledWith({
+ webhookUrl: '',
+ channel: '',
+ content: '',
+ });
+});
diff --git a/providers/rocket-chat/src/lib/rocket-chat.provider.ts b/providers/rocket-chat/src/lib/rocket-chat.provider.ts
new file mode 100644
index 00000000000..0ef276e348c
--- /dev/null
+++ b/providers/rocket-chat/src/lib/rocket-chat.provider.ts
@@ -0,0 +1,46 @@
+import {
+ ChannelTypeEnum,
+ ISendMessageSuccessResponse,
+ IChatOptions,
+ IChatProvider,
+} from '@novu/stateless';
+import axios from 'axios';
+
+export class RocketChatProvider implements IChatProvider {
+ id = 'rocket-chat';
+ channelType = ChannelTypeEnum.CHAT as ChannelTypeEnum.CHAT;
+ private axiosInstance = axios.create();
+
+ constructor(
+ private config: {
+ token: string;
+ user: string;
+ }
+ ) {}
+
+ async sendMessage(
+ options: IChatOptions
+ ): Promise {
+ const roomId = options.channel;
+ const payload = {
+ message: {
+ rid: roomId,
+ msg: options.content,
+ },
+ };
+ const headers = {
+ 'x-auth-token': this.config.token,
+ 'x-user-id': this.config.user,
+ 'Content-Type': 'application/json',
+ };
+ const baseURL = `${options.webhookUrl.toString()}/api/v1/chat.sendMessage`;
+ const { data } = await this.axiosInstance.post(baseURL, payload, {
+ headers: headers,
+ });
+
+ return {
+ id: data.message._id,
+ date: data.message.ts,
+ };
+ }
+}
diff --git a/providers/rocket-chat/tsconfig.json b/providers/rocket-chat/tsconfig.json
new file mode 100644
index 00000000000..5b8120fea36
--- /dev/null
+++ b/providers/rocket-chat/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "build/main",
+ "rootDir": "src",
+ "types": ["node", "jest"]
+ },
+ "include": ["src/**/*.ts"],
+ "exclude": ["node_modules/**"]
+}
diff --git a/providers/rocket-chat/tsconfig.module.json b/providers/rocket-chat/tsconfig.module.json
new file mode 100644
index 00000000000..79be3a5c40b
--- /dev/null
+++ b/providers/rocket-chat/tsconfig.module.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./tsconfig",
+ "compilerOptions": {
+ "target": "esnext",
+ "outDir": "build/module",
+ "module": "esnext"
+ },
+ "exclude": ["node_modules/**"]
+}