@ethora/sdk-backend 25.12.20 → 26.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @format
9
9
  */
10
- import type { UUID, ChatRepository, ApiResponse, UpdateUserData, GetUsersQueryParams } from "../types";
10
+ import type { UUID, ChatRepository, ApiResponse, UpdateUserData, GetUsersQueryParams } from '../types';
11
11
  /**
12
12
  * EthoraSDKService - Concrete implementation of ChatRepository
13
13
  */
@@ -17,16 +17,16 @@ export declare class EthoraSDKService implements ChatRepository {
17
17
  private readonly httpClient;
18
18
  constructor();
19
19
  /**
20
- * Generates a fully-qualified chat room JID from a workspace ID
20
+ * Generates a fully-qualified chat room JID from a chat ID
21
21
  *
22
- * The JID is constructed in the format `<appId>_<workspace_id>@conference.xmpp.ethoradev.com`.
22
+ * The JID is constructed in the format `<appId>_<chat_id>@conference.xmpp.ethoradev.com`.
23
23
  * This method uses a static JID domain to provide a unique identifier for a chat room.
24
24
  *
25
- * @param workspaceId - The unique identifier of the workspace
25
+ * @param chatId - The unique identifier of the chat
26
26
  * @param full - Whether to include the full JID domain
27
27
  * @returns The fully-qualified JID string for the chat room
28
28
  */
29
- createChatName(workspaceId: UUID, full?: boolean): string;
29
+ createChatName(chatId: UUID, full?: boolean): string;
30
30
  /**
31
31
  * Creates a client-side JWT token for a specific user ID
32
32
  *
@@ -64,30 +64,36 @@ export declare class EthoraSDKService implements ChatRepository {
64
64
  */
65
65
  createUser(userId: UUID, userData?: Record<string, unknown>): Promise<ApiResponse>;
66
66
  /**
67
- * Creates a chat room for a workspace
67
+ * Creates a chat room
68
68
  *
69
- * @param workspaceId - The unique identifier of the workspace
69
+ * @param chatId - The unique identifier of the chat
70
70
  * @param roomData - Additional room data (optional)
71
71
  * @returns The API response
72
72
  */
73
- createChatRoom(workspaceId: UUID, roomData?: Record<string, unknown>): Promise<ApiResponse>;
73
+ createChatRoom(chatId: UUID, roomData?: Record<string, unknown>): Promise<ApiResponse>;
74
74
  /**
75
75
  * Grants a user access to a chat room
76
76
  *
77
- * Uses plain userIds without prefixing (matching how users are created)
78
- *
79
- * @param workspaceId - The unique identifier of the workspace
77
+ * @param chatId - The unique identifier of the chat
80
78
  * @param userId - The unique identifier of the user (or array of user IDs)
81
79
  * @returns The API response
82
80
  */
83
- grantUserAccessToChatRoom(workspaceId: UUID, userId: UUID | UUID[]): Promise<ApiResponse>;
81
+ grantUserAccessToChatRoom(chatId: UUID, userId: UUID | UUID[]): Promise<ApiResponse>;
84
82
  /**
85
83
  * Grants chatbot access to a chat room
86
84
  *
87
- * @param workspaceId - The unique identifier of the workspace
85
+ * @param chatId - The unique identifier of the chat
86
+ * @returns The API response
87
+ */
88
+ grantChatbotAccessToChatRoom(chatId: UUID): Promise<ApiResponse>;
89
+ /**
90
+ * Removes a user's access to a chat room
91
+ *
92
+ * @param chatId - The unique identifier of the chat
93
+ * @param userId - The unique identifier of the user (or array of user IDs)
88
94
  * @returns The API response
89
95
  */
90
- grantChatbotAccessToChatRoom(workspaceId: UUID): Promise<ApiResponse>;
96
+ removeUserAccessFromChatRoom(chatId: UUID, userId: UUID | UUID[]): Promise<ApiResponse>;
91
97
  /**
92
98
  * Deletes users from the chat service
93
99
  *
@@ -96,16 +102,16 @@ export declare class EthoraSDKService implements ChatRepository {
96
102
  */
97
103
  deleteUsers(userIds: UUID[]): Promise<ApiResponse>;
98
104
  /**
99
- * Deletes a chat room from the chat service by its workspace ID
105
+ * Deletes a chat room from the chat service by its chat ID
100
106
  *
101
- * This method sends a DELETE request to the Ethora API using the workspace ID
107
+ * This method sends a DELETE request to the Ethora API using the chat ID
102
108
  * to construct the chat name. It gracefully handles the case where the chat room
103
109
  * is already non-existent (422 Not Found).
104
110
  *
105
- * @param workspaceId - The unique identifier of the workspace associated with the chat room
111
+ * @param chatId - The unique identifier of the chat associated with the chat room
106
112
  * @returns The JSON response from the chat service upon successful deletion or a success status if not found
107
113
  */
108
- deleteChatRoom(workspaceId: UUID): Promise<ApiResponse>;
114
+ deleteChatRoom(chatId: UUID): Promise<ApiResponse>;
109
115
  /**
110
116
  * Updates multiple users in the chat service
111
117
  *
@@ -119,6 +125,9 @@ export declare class EthoraSDKService implements ChatRepository {
119
125
  * - not-found: user was not found
120
126
  * - skipped: user update was skipped
121
127
  *
128
+ * Note: The API only accepts xmppUsername, firstName, lastName, username, and profileImage fields.
129
+ * Other fields will be filtered out automatically.
130
+ *
122
131
  * @param users - Array of user data to update (1-100 users)
123
132
  * @returns The API response with results array containing status for each user
124
133
  */
@@ -133,6 +142,9 @@ export declare class EthoraSDKService implements ChatRepository {
133
142
  * - For 1-on-1 chats: use xmppUsernameA-xmppUsernameB format
134
143
  * - xmppUsername parameter: returns a specific user by XMPP username
135
144
  *
145
+ * Note: The xmppUsername query parameter may not be supported by the API yet.
146
+ * In that case, you can get all users and filter client-side.
147
+ *
136
148
  * @param params - Query parameters for filtering users (optional)
137
149
  * @returns The API response
138
150
  */
@@ -1 +1 @@
1
- {"version":3,"file":"EthoraSDKService.d.ts","sourceRoot":"","sources":["../../src/repositories/EthoraSDKService.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACV,IAAI,EACJ,cAAc,EACd,WAAW,EAIX,cAAc,EAEd,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAWlB;;GAEG;AACH,qBAAa,gBAAiB,YAAW,cAAc;IACrD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;;IAgB3C;;;;;;;;;OASG;IACH,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,GAAE,OAAc,GAAG,MAAM;IAa/D;;;;;;;;;OASG;IACH,sBAAsB,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM;IAK5C;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAOlB;;;;;OAKG;YACW,WAAW;IA4BzB;;;;;;;;;;OAUG;IACG,UAAU,CACd,MAAM,EAAE,IAAI,EACZ,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC;IA0EvB;;;;;;OAMG;IACG,cAAc,CAClB,WAAW,EAAE,IAAI,EACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC;IAwBvB;;;;;;;;OAQG;IACG,yBAAyB,CAC7B,WAAW,EAAE,IAAI,EACjB,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,GACpB,OAAO,CAAC,WAAW,CAAC;IA8CvB;;;;;OAKG;IACG,4BAA4B,CAAC,WAAW,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IAoB3E;;;;;OAKG;IACG,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAkCxD;;;;;;;;;OASG;IACG,cAAc,CAAC,WAAW,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IAgD7D;;;;;;;;;;;;;;;OAeG;IACG,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAkChE;;;;;;;;;;;;OAYG;IACG,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC;CA6BnE;AAaD,wBAAgB,mBAAmB,IAAI,gBAAgB,CAMtD"}
1
+ {"version":3,"file":"EthoraSDKService.d.ts","sourceRoot":"","sources":["../../src/repositories/EthoraSDKService.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACV,IAAI,EACJ,cAAc,EACd,WAAW,EAIX,cAAc,EAEd,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAWlB;;GAEG;AACH,qBAAa,gBAAiB,YAAW,cAAc;IACrD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;;IAgB3C;;;;;;;;;OASG;IACH,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,GAAE,OAAc,GAAG,MAAM;IAW1D;;;;;;;;;OASG;IACH,sBAAsB,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM;IAK5C;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAOlB;;;;;OAKG;YACW,WAAW;IA4BzB;;;;;;;;;;OAUG;IACG,UAAU,CACd,MAAM,EAAE,IAAI,EACZ,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC;IA0EvB;;;;;;OAMG;IACG,cAAc,CAClB,MAAM,EAAE,IAAI,EACZ,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC;IAsBvB;;;;;;OAMG;IACG,yBAAyB,CAC7B,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,GACpB,OAAO,CAAC,WAAW,CAAC;IAsDvB;;;;;OAKG;IACG,4BAA4B,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IAkBtE;;;;;;OAMG;IACG,4BAA4B,CAChC,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,GACpB,OAAO,CAAC,WAAW,CAAC;IAoDvB;;;;;OAKG;IACG,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAkCxD;;;;;;;;;OASG;IACG,cAAc,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IA8CxD;;;;;;;;;;;;;;;;;;OAkBG;IACG,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IA2ChE;;;;;;;;;;;;;;;OAeG;IACG,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC;CA6BnE;AAaD,wBAAgB,mBAAmB,IAAI,gBAAgB,CAMtD"}
@@ -19,7 +19,7 @@ const axios_1 = __importDefault(require("axios"));
19
19
  const secrets_1 = require("../config/secrets");
20
20
  const jwt_1 = require("../utils/jwt");
21
21
  const logger_1 = require("../utils/logger");
22
- const logger = (0, logger_1.getLogger)("EthoraSDKService");
22
+ const logger = (0, logger_1.getLogger)('EthoraSDKService');
23
23
  /**
24
24
  * EthoraSDKService - Concrete implementation of ChatRepository
25
25
  */
@@ -31,26 +31,26 @@ class EthoraSDKService {
31
31
  this.httpClient = axios_1.default.create({
32
32
  timeout: secrets_1.DEFAULT_TIMEOUT.total,
33
33
  headers: {
34
- "Content-Type": "application/json",
34
+ 'Content-Type': 'application/json',
35
35
  },
36
36
  });
37
- logger.debug("EthoraSDKService instance initialized");
37
+ logger.debug('EthoraSDKService instance initialized');
38
38
  }
39
39
  /**
40
- * Generates a fully-qualified chat room JID from a workspace ID
40
+ * Generates a fully-qualified chat room JID from a chat ID
41
41
  *
42
- * The JID is constructed in the format `<appId>_<workspace_id>@conference.xmpp.ethoradev.com`.
42
+ * The JID is constructed in the format `<appId>_<chat_id>@conference.xmpp.ethoradev.com`.
43
43
  * This method uses a static JID domain to provide a unique identifier for a chat room.
44
44
  *
45
- * @param workspaceId - The unique identifier of the workspace
45
+ * @param chatId - The unique identifier of the chat
46
46
  * @param full - Whether to include the full JID domain
47
47
  * @returns The fully-qualified JID string for the chat room
48
48
  */
49
- createChatName(workspaceId, full = true) {
50
- logger.debug(`Creating chat room name (JID) for workspace ID: ${workspaceId}`);
49
+ createChatName(chatId, full = true) {
50
+ logger.debug(`Creating chat room name (JID) for chat ID: ${chatId}`);
51
51
  const chatName = full
52
- ? `${this.secrets.chatAppId}_${workspaceId}${secrets_1.ETHORA_JID_DOMAIN}`
53
- : `${this.secrets.chatAppId}_${workspaceId}`;
52
+ ? `${this.secrets.chatAppId}_${chatId}${secrets_1.ETHORA_JID_DOMAIN}`
53
+ : `${this.secrets.chatAppId}_${chatId}`;
54
54
  logger.info(`Chat room name created: '${chatName}'`);
55
55
  return chatName;
56
56
  }
@@ -74,9 +74,9 @@ class EthoraSDKService {
74
74
  * @returns The headers dictionary containing the `x-custom-token` field
75
75
  */
76
76
  getHeaders() {
77
- logger.debug("Retrieving headers for a server-to-server API call");
77
+ logger.debug('Retrieving headers for a server-to-server API call');
78
78
  return {
79
- "x-custom-token": (0, jwt_1.createServerToken)(),
79
+ 'x-custom-token': (0, jwt_1.createServerToken)(),
80
80
  };
81
81
  }
82
82
  /**
@@ -104,7 +104,7 @@ class EthoraSDKService {
104
104
  `Response: ${axiosError.response?.data}`, error);
105
105
  throw error;
106
106
  }
107
- logger.error("An unexpected error occurred during API call", error);
107
+ logger.error('An unexpected error occurred during API call', error);
108
108
  throw error;
109
109
  }
110
110
  }
@@ -121,7 +121,7 @@ class EthoraSDKService {
121
121
  */
122
122
  async createUser(userId, userData) {
123
123
  logger.info(`Attempting to create user with ID: ${userId}`);
124
- const createUrl = `${this.baseEthoraUrl}/v1/users/batch`;
124
+ const createUrl = `${this.baseEthoraUrl}/v2/users/batch`;
125
125
  // Extract user fields from userData or use defaults
126
126
  // Generate unique email using UUID if not provided
127
127
  const email = userData?.email || `${(0, crypto_1.randomUUID)()}@example.com`;
@@ -132,22 +132,22 @@ class EthoraSDKService {
132
132
  let lastName;
133
133
  if (userData?.firstName) {
134
134
  firstName = userData.firstName;
135
- lastName = userData?.lastName || "";
135
+ lastName = userData?.lastName || '';
136
136
  }
137
137
  else if (userData?.displayName) {
138
138
  const displayName = userData.displayName;
139
139
  const nameParts = displayName.trim().split(/\s+/);
140
- firstName = nameParts[0] || "User";
141
- lastName = nameParts.slice(1).join(" ") || "";
140
+ firstName = nameParts[0] || 'User';
141
+ lastName = nameParts.slice(1).join(' ') || '';
142
142
  }
143
143
  else {
144
- firstName = "User";
145
- lastName = "";
144
+ firstName = 'User';
145
+ lastName = '';
146
146
  }
147
147
  // Ensure lastName meets API requirements: at least 2 characters, not empty
148
148
  if (!lastName || lastName.length < 2) {
149
149
  // Use a default lastName if empty or too short
150
- lastName = "User";
150
+ lastName = 'User';
151
151
  }
152
152
  // Use plain userId without prefixing
153
153
  const userIdStr = String(userId);
@@ -162,13 +162,13 @@ class EthoraSDKService {
162
162
  password: password,
163
163
  ...(userData &&
164
164
  Object.fromEntries(Object.entries(userData).filter(([key]) => ![
165
- "email",
166
- "firstName",
167
- "lastName",
168
- "password",
169
- "uuid",
170
- "displayName",
171
- "role", // Role is not allowed in user creation payload
165
+ 'email',
166
+ 'firstName',
167
+ 'lastName',
168
+ 'password',
169
+ 'uuid',
170
+ 'displayName',
171
+ 'role',
172
172
  ].includes(key)))),
173
173
  },
174
174
  ],
@@ -176,32 +176,32 @@ class EthoraSDKService {
176
176
  logger.debug(`Chat service API URL: ${createUrl}`);
177
177
  logger.debug(`Request payload: ${JSON.stringify(payload)}`);
178
178
  return this.makeRequest({
179
- method: "POST",
179
+ method: 'POST',
180
180
  url: createUrl,
181
181
  data: payload,
182
182
  });
183
183
  }
184
184
  /**
185
- * Creates a chat room for a workspace
185
+ * Creates a chat room
186
186
  *
187
- * @param workspaceId - The unique identifier of the workspace
187
+ * @param chatId - The unique identifier of the chat
188
188
  * @param roomData - Additional room data (optional)
189
189
  * @returns The API response
190
190
  */
191
- async createChatRoom(workspaceId, roomData) {
192
- logger.info(`Attempting to create chat room for workspace ID: ${workspaceId}`);
193
- const createUrl = `${this.baseEthoraUrl}/v1/chats`;
191
+ async createChatRoom(chatId, roomData) {
192
+ logger.info(`Attempting to create chat room with ID: ${chatId}`);
193
+ const createUrl = `${this.baseEthoraUrl}/v2/chats`;
194
194
  // Create chat room - API expects title, uuid, and type
195
195
  const payload = {
196
- title: roomData?.title || `Chat Room ${workspaceId}`,
197
- uuid: String(workspaceId),
198
- type: roomData?.type || "group",
196
+ title: roomData?.title || `Chat Room ${chatId}`,
197
+ uuid: String(chatId), // Use chatId as uuid
198
+ type: roomData?.type || 'group',
199
199
  ...roomData, // Allow roomData to override fields if provided
200
200
  };
201
201
  logger.debug(`Chat service API URL: ${createUrl}`);
202
202
  logger.debug(`Request payload: ${JSON.stringify(payload)}`);
203
203
  return this.makeRequest({
204
- method: "POST",
204
+ method: 'POST',
205
205
  url: createUrl,
206
206
  data: payload,
207
207
  });
@@ -209,32 +209,42 @@ class EthoraSDKService {
209
209
  /**
210
210
  * Grants a user access to a chat room
211
211
  *
212
- * Uses plain userIds without prefixing (matching how users are created)
213
- *
214
- * @param workspaceId - The unique identifier of the workspace
212
+ * @param chatId - The unique identifier of the chat
215
213
  * @param userId - The unique identifier of the user (or array of user IDs)
216
214
  * @returns The API response
217
215
  */
218
- async grantUserAccessToChatRoom(workspaceId, userId) {
219
- logger.info(`Granting user(s) access to chat room for workspace ${workspaceId}`);
220
- const chatName = this.createChatName(workspaceId, false);
221
- // Use /v1/chats/users-access endpoint with chatName and members array
222
- const grantUrl = `${this.baseEthoraUrl}/v1/chats/users-access`;
216
+ async grantUserAccessToChatRoom(chatId, userId) {
217
+ logger.info(`Granting user(s) access to chat room ${chatId}`);
218
+ const chatName = this.createChatName(chatId, false);
219
+ // Use /v2/chats/users-access endpoint with chatName and members array
220
+ const grantUrl = `${this.baseEthoraUrl}/v2/chats/users-access`;
223
221
  // Convert single userId to array if needed
224
- // Use plain userIds without prefixing
222
+ // API requires usernames to start with appId, so prefix them
225
223
  const members = Array.isArray(userId)
226
- ? userId.map((id) => String(id))
227
- : [String(userId)];
224
+ ? userId.map((id) => {
225
+ const userIdStr = String(id);
226
+ // If userId doesn't start with appId, prefix it
227
+ return userIdStr.startsWith(this.secrets.chatAppId)
228
+ ? userIdStr
229
+ : `${this.secrets.chatAppId}_${userIdStr}`;
230
+ })
231
+ : [
232
+ (() => {
233
+ const userIdStr = String(userId);
234
+ return userIdStr.startsWith(this.secrets.chatAppId)
235
+ ? userIdStr
236
+ : `${this.secrets.chatAppId}_${userIdStr}`;
237
+ })(),
238
+ ];
228
239
  const payload = {
229
240
  chatName: chatName,
230
241
  members: members,
231
242
  };
232
243
  logger.debug(`Chat service API URL: ${grantUrl}`);
233
244
  logger.debug(`Request payload: ${JSON.stringify(payload)}`);
234
- logger.debug(`Using XMPP usernames for members: ${members.join(", ")}`);
235
245
  try {
236
246
  return await this.makeRequest({
237
- method: "POST",
247
+ method: 'POST',
238
248
  url: grantUrl,
239
249
  data: payload,
240
250
  });
@@ -243,8 +253,6 @@ class EthoraSDKService {
243
253
  if (axios_1.default.isAxiosError(error)) {
244
254
  const errorData = error.response?.data;
245
255
  logger.error(`Failed to grant user access. Status: ${error.response?.status}, Response: ${JSON.stringify(errorData)}`, error);
246
- // Log the XMPP usernames that were attempted for debugging
247
- logger.debug(`Attempted XMPP usernames: ${members.join(", ")}`);
248
256
  }
249
257
  throw error;
250
258
  }
@@ -252,20 +260,69 @@ class EthoraSDKService {
252
260
  /**
253
261
  * Grants chatbot access to a chat room
254
262
  *
255
- * @param workspaceId - The unique identifier of the workspace
263
+ * @param chatId - The unique identifier of the chat
256
264
  * @returns The API response
257
265
  */
258
- async grantChatbotAccessToChatRoom(workspaceId) {
259
- logger.info(`Granting chatbot access to chat room for workspace ${workspaceId}`);
266
+ async grantChatbotAccessToChatRoom(chatId) {
267
+ logger.info(`Granting chatbot access to chat room ${chatId}`);
260
268
  if (!this.secrets.chatBotJid) {
261
- const error = new Error("Chatbot JID not configured. Set ETHORA_CHAT_BOT_JID environment variable.");
262
- logger.error("Cannot grant chatbot access", error);
269
+ const error = new Error('Chatbot JID not configured. Set ETHORA_CHAT_BOT_JID environment variable.');
270
+ logger.error('Cannot grant chatbot access', error);
263
271
  throw error;
264
272
  }
265
273
  // Extract username from JID (format: "username@domain" -> "username")
266
- const chatbotUsername = this.secrets.chatBotJid.split("@")[0];
274
+ const chatbotUsername = this.secrets.chatBotJid.split('@')[0];
267
275
  // Use the same grant access method with chatbot JID
268
- return this.grantUserAccessToChatRoom(workspaceId, chatbotUsername);
276
+ return this.grantUserAccessToChatRoom(chatId, chatbotUsername);
277
+ }
278
+ /**
279
+ * Removes a user's access to a chat room
280
+ *
281
+ * @param chatId - The unique identifier of the chat
282
+ * @param userId - The unique identifier of the user (or array of user IDs)
283
+ * @returns The API response
284
+ */
285
+ async removeUserAccessFromChatRoom(chatId, userId) {
286
+ logger.info(`Removing user(s) access from chat room ${chatId}`);
287
+ const chatName = this.createChatName(chatId, false);
288
+ // Use /v2/chats/usersAccess/remove DELETE endpoint (fallback to legacy /v2/chats/users-access)
289
+ const revokeUrl = `${this.baseEthoraUrl}/v2/chats/users-access`;
290
+ // Convert single userId to array if needed
291
+ const members = Array.isArray(userId)
292
+ ? userId.map((id) => {
293
+ const userIdStr = String(id);
294
+ return userIdStr.startsWith(this.secrets.chatAppId)
295
+ ? userIdStr
296
+ : `${this.secrets.chatAppId}_${userIdStr}`;
297
+ })
298
+ : [
299
+ (() => {
300
+ const userIdStr = String(userId);
301
+ return userIdStr.startsWith(this.secrets.chatAppId)
302
+ ? userIdStr
303
+ : `${this.secrets.chatAppId}_${userIdStr}`;
304
+ })(),
305
+ ];
306
+ const payload = {
307
+ chatName: chatName,
308
+ members: members,
309
+ };
310
+ logger.debug(`Chat service API URL: ${revokeUrl}`);
311
+ logger.debug(`Request payload: ${JSON.stringify(payload)}`);
312
+ try {
313
+ return await this.makeRequest({
314
+ method: 'DELETE',
315
+ url: revokeUrl,
316
+ data: payload,
317
+ });
318
+ }
319
+ catch (error) {
320
+ if (axios_1.default.isAxiosError(error)) {
321
+ const errorData = error.response?.data;
322
+ logger.error(`Failed to remove user access. Status: ${error.response?.status}, Response: ${JSON.stringify(errorData)}`, error);
323
+ }
324
+ throw error;
325
+ }
269
326
  }
270
327
  /**
271
328
  * Deletes users from the chat service
@@ -274,7 +331,7 @@ class EthoraSDKService {
274
331
  * @returns The API response
275
332
  */
276
333
  async deleteUsers(userIds) {
277
- logger.info(`Attempting to delete users: ${userIds.join(", ")}`);
334
+ logger.info(`Attempting to delete users: ${userIds.join(', ')}`);
278
335
  const deleteUrl = `${this.baseEthoraUrl}/v1/users/batch`;
279
336
  const payload = {
280
337
  usersIdList: userIds.map((id) => String(id)),
@@ -283,7 +340,7 @@ class EthoraSDKService {
283
340
  logger.debug(`Request payload: ${JSON.stringify(payload)}`);
284
341
  try {
285
342
  return await this.makeRequest({
286
- method: "DELETE",
343
+ method: 'DELETE',
287
344
  url: deleteUrl,
288
345
  data: payload,
289
346
  });
@@ -292,29 +349,29 @@ class EthoraSDKService {
292
349
  // Handle the case where users don't exist (422 with "not found")
293
350
  if (axios_1.default.isAxiosError(error) &&
294
351
  error.response?.status === 422 &&
295
- typeof error.response.data === "string" &&
296
- error.response.data.includes("not found")) {
297
- logger.info("No users to delete from the chat service. The request contained non-existent users.");
352
+ typeof error.response.data === 'string' &&
353
+ error.response.data.includes('not found')) {
354
+ logger.info('No users to delete from the chat service. The request contained non-existent users.');
298
355
  return { ok: false };
299
356
  }
300
357
  throw error;
301
358
  }
302
359
  }
303
360
  /**
304
- * Deletes a chat room from the chat service by its workspace ID
361
+ * Deletes a chat room from the chat service by its chat ID
305
362
  *
306
- * This method sends a DELETE request to the Ethora API using the workspace ID
363
+ * This method sends a DELETE request to the Ethora API using the chat ID
307
364
  * to construct the chat name. It gracefully handles the case where the chat room
308
365
  * is already non-existent (422 Not Found).
309
366
  *
310
- * @param workspaceId - The unique identifier of the workspace associated with the chat room
367
+ * @param chatId - The unique identifier of the chat associated with the chat room
311
368
  * @returns The JSON response from the chat service upon successful deletion or a success status if not found
312
369
  */
313
- async deleteChatRoom(workspaceId) {
314
- logger.info(`Attempting to delete chat room for workspace ID: ${workspaceId}`);
370
+ async deleteChatRoom(chatId) {
371
+ logger.info(`Attempting to delete chat room with ID: ${chatId}`);
315
372
  const deleteUrl = `${this.baseEthoraUrl}/v1/chats`;
316
373
  // We must use the short name when deleting the chat room
317
- const chatName = this.createChatName(workspaceId, false);
374
+ const chatName = this.createChatName(chatId, false);
318
375
  const payload = {
319
376
  name: chatName,
320
377
  };
@@ -322,7 +379,7 @@ class EthoraSDKService {
322
379
  logger.debug(`Request payload: ${JSON.stringify(payload)}`);
323
380
  try {
324
381
  const response = await this.makeRequest({
325
- method: "DELETE",
382
+ method: 'DELETE',
326
383
  url: deleteUrl,
327
384
  data: payload,
328
385
  });
@@ -336,10 +393,10 @@ class EthoraSDKService {
336
393
  const responseText = axiosError.response?.data;
337
394
  // Handle the case where the room does not exist (Ethora returns 422 with "not found" in body)
338
395
  if (statusCode === 422 &&
339
- typeof responseText === "string" &&
340
- responseText.toLowerCase().includes("not found")) {
396
+ typeof responseText === 'string' &&
397
+ responseText.toLowerCase().includes('not found')) {
341
398
  logger.warn(`Chat room '${chatName}' not found during deletion attempt (Ignored 422)`);
342
- return { ok: false, reason: "Chat room not found" };
399
+ return { ok: false, reason: 'Chat room not found' };
343
400
  }
344
401
  }
345
402
  // Re-throw if it's not a "not found" case
@@ -359,24 +416,39 @@ class EthoraSDKService {
359
416
  * - not-found: user was not found
360
417
  * - skipped: user update was skipped
361
418
  *
419
+ * Note: The API only accepts xmppUsername, firstName, lastName, username, and profileImage fields.
420
+ * Other fields will be filtered out automatically.
421
+ *
362
422
  * @param users - Array of user data to update (1-100 users)
363
423
  * @returns The API response with results array containing status for each user
364
424
  */
365
425
  async updateUsers(users) {
366
426
  // Validate user count limit
367
427
  if (users.length === 0) {
368
- throw new Error("At least 1 user is required for update");
428
+ throw new Error('At least 1 user is required for update');
369
429
  }
370
430
  if (users.length > 100) {
371
- throw new Error("Maximum 100 users allowed per update request");
431
+ throw new Error('Maximum 100 users allowed per update request');
372
432
  }
373
433
  logger.info(`Attempting to update ${users.length} user(s)`);
374
- const updateUrl = `${this.baseEthoraUrl}/v1/chats/users`;
434
+ const updateUrl = `${this.baseEthoraUrl}/v2/chats/users`;
375
435
  // Remove userId from payload if present, as API doesn't accept it
376
436
  // API expects xmppUsername or other identifier fields instead
437
+ // Also filter to only include allowed fields: xmppUsername, firstName, lastName, username, profileImage
377
438
  const cleanedUsers = users.map((user) => {
378
439
  const { userId, ...rest } = user;
379
- return rest;
440
+ const allowedFields = {};
441
+ if (rest.xmppUsername)
442
+ allowedFields.xmppUsername = rest.xmppUsername;
443
+ if (rest.firstName)
444
+ allowedFields.firstName = rest.firstName;
445
+ if (rest.lastName)
446
+ allowedFields.lastName = rest.lastName;
447
+ if (rest.username)
448
+ allowedFields.username = rest.username;
449
+ if (rest.profileImage)
450
+ allowedFields.profileImage = rest.profileImage;
451
+ return allowedFields;
380
452
  });
381
453
  const payload = {
382
454
  users: cleanedUsers,
@@ -384,7 +456,7 @@ class EthoraSDKService {
384
456
  logger.debug(`Chat service API URL: ${updateUrl}`);
385
457
  logger.debug(`Request payload: ${JSON.stringify(payload)}`);
386
458
  return this.makeRequest({
387
- method: "PATCH",
459
+ method: 'PATCH',
388
460
  url: updateUrl,
389
461
  data: payload,
390
462
  });
@@ -399,11 +471,14 @@ class EthoraSDKService {
399
471
  * - For 1-on-1 chats: use xmppUsernameA-xmppUsernameB format
400
472
  * - xmppUsername parameter: returns a specific user by XMPP username
401
473
  *
474
+ * Note: The xmppUsername query parameter may not be supported by the API yet.
475
+ * In that case, you can get all users and filter client-side.
476
+ *
402
477
  * @param params - Query parameters for filtering users (optional)
403
478
  * @returns The API response
404
479
  */
405
480
  async getUsers(params) {
406
- const getUrl = `${this.baseEthoraUrl}/v1/chats/users`;
481
+ const getUrl = `${this.baseEthoraUrl}/v2/chats/users`;
407
482
  // Build query parameters
408
483
  const queryParams = [];
409
484
  if (params?.chatName) {
@@ -412,13 +487,13 @@ class EthoraSDKService {
412
487
  if (params?.xmppUsername) {
413
488
  queryParams.push(`xmppUsername=${encodeURIComponent(params.xmppUsername)}`);
414
489
  }
415
- const urlWithParams = queryParams.length > 0 ? `${getUrl}?${queryParams.join("&")}` : getUrl;
490
+ const urlWithParams = queryParams.length > 0 ? `${getUrl}?${queryParams.join('&')}` : getUrl;
416
491
  logger.info(params
417
492
  ? `Getting users with params: ${JSON.stringify(params)}`
418
- : "Getting all users of the app");
493
+ : 'Getting all users of the app');
419
494
  logger.debug(`Chat service API URL: ${urlWithParams}`);
420
495
  return this.makeRequest({
421
- method: "GET",
496
+ method: 'GET',
422
497
  url: urlWithParams,
423
498
  });
424
499
  }
@@ -436,7 +511,7 @@ exports.EthoraSDKService = EthoraSDKService;
436
511
  let repositoryInstance = null;
437
512
  function getEthoraSDKService() {
438
513
  if (!repositoryInstance) {
439
- logger.debug("Creating new EthoraSDKService instance");
514
+ logger.debug('Creating new EthoraSDKService instance');
440
515
  repositoryInstance = new EthoraSDKService();
441
516
  }
442
517
  return repositoryInstance;