blob: 283e527ee501b9196d24e278c12515e8d890817e [file] [log] [blame]
Issam E. Maghnif796a092022-10-09 20:25:26 +00001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
Charlie2bc0d672022-11-04 11:53:44 -040018import { AccountDetails, Message, VolatileDetails, WebSocketMessageType } from 'jami-web-common';
Issam E. Maghnif796a092022-10-09 20:25:26 +000019import log from 'loglevel';
Misha Krieger-Raynauld6f9c7ae2022-10-28 11:41:45 -040020import { filter, firstValueFrom, map, Subject } from 'rxjs';
Issam E. Maghnif796a092022-10-09 20:25:26 +000021import { Service } from 'typedi';
22
Charlie2bc0d672022-11-04 11:53:44 -040023import { Ws } from '../ws.js';
Misha Krieger-Raynauldaddd6fe2022-10-22 12:46:04 -040024import { JamiSignal } from './jami-signal.js';
25import {
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -040026 AccountDetailsChanged,
27 ConversationLoaded,
28 ConversationReady,
29 ConversationRemoved,
Charlieb62c6782022-10-30 15:14:56 -040030 IncomingAccountMessage,
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -040031 KnownDevicesChanged,
32 MessageReceived,
Misha Krieger-Raynauldaddd6fe2022-10-22 12:46:04 -040033 NameRegistrationEnded,
34 RegisteredNameFound,
35 RegistrationStateChanged,
36 VolatileDetailsChanged,
37} from './jami-signal-interfaces.js';
Misha Krieger-Raynauldbfed1732022-11-01 20:49:35 -040038import { JamiSwig, StringMap, stringMapToRecord, stringVectToArray, vectMapToRecordArray } from './jami-swig.js';
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -040039import { require } from './utils.js';
Issam E. Maghnif796a092022-10-09 20:25:26 +000040
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -040041// TODO: Mechanism to map account IDs to a list of WebSockets
Misha Krieger-Raynauldbfed1732022-11-01 20:49:35 -040042// TODO: Convert Records to interfaces and replace them in common/ (e.g. Contact)
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -040043
Issam E. Maghnif796a092022-10-09 20:25:26 +000044@Service()
45export class Jamid {
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -040046 private readonly jamiSwig: JamiSwig;
47 private readonly usernamesToAccountIds: Map<string, string>;
Issam E. Maghnif796a092022-10-09 20:25:26 +000048 private readonly events;
49
Charlie2bc0d672022-11-04 11:53:44 -040050 constructor(private ws: Ws) {
Issam E. Maghnif796a092022-10-09 20:25:26 +000051 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
simon7d4386c2022-10-26 17:47:59 -040052 this.jamiSwig = require('../../jamid.node') as JamiSwig; // TODO: we should put the path in the .env
Issam E. Maghnif796a092022-10-09 20:25:26 +000053
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -040054 this.usernamesToAccountIds = new Map<string, string>();
55
56 // Setup signal handlers
Issam E. Maghnif796a092022-10-09 20:25:26 +000057 const handlers: Record<string, unknown> = {};
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -040058
59 // Add default handler for all signals
60 const createDefaultHandler = (signal: string) => {
61 return (...args: unknown[]) => log.warn('Unhandled', signal, args);
Issam E. Maghnif796a092022-10-09 20:25:26 +000062 };
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -040063 for (const signal in JamiSignal) {
64 handlers[signal] = createDefaultHandler(signal);
65 }
66
67 // Overwrite handlers for handled signals using RxJS Subjects, converting multiple arguments to objects
68 const onAccountsChanged = new Subject<void>();
69 handlers.AccountsChanged = () => onAccountsChanged.next();
70
71 const onAccountDetailsChanged = new Subject<AccountDetailsChanged>();
72 handlers.AccountDetailsChanged = (accountId: string, details: AccountDetails) =>
73 onAccountDetailsChanged.next({ accountId, details });
Issam E. Maghnif796a092022-10-09 20:25:26 +000074
75 const onVolatileDetailsChanged = new Subject<VolatileDetailsChanged>();
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -040076 handlers.VolatileDetailsChanged = (accountId: string, details: VolatileDetails) =>
77 onVolatileDetailsChanged.next({ accountId, details });
Issam E. Maghnif796a092022-10-09 20:25:26 +000078
79 const onRegistrationStateChanged = new Subject<RegistrationStateChanged>();
80 handlers.RegistrationStateChanged = (accountId: string, state: string, code: number, details: string) =>
81 onRegistrationStateChanged.next({ accountId, state, code, details });
82
83 const onNameRegistrationEnded = new Subject<NameRegistrationEnded>();
84 handlers.NameRegistrationEnded = (accountId: string, state: number, username: string) =>
85 onNameRegistrationEnded.next({ accountId, state, username });
86
87 const onRegisteredNameFound = new Subject<RegisteredNameFound>();
88 handlers.RegisteredNameFound = (accountId: string, state: number, address: string, username: string) =>
89 onRegisteredNameFound.next({ accountId, state, address, username });
90
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -040091 const onKnownDevicesChanged = new Subject<KnownDevicesChanged>();
92 handlers.KnownDevicesChanged = (accountId: string, devices: Record<string, string>) =>
93 onKnownDevicesChanged.next({ accountId, devices });
94
Charlieb62c6782022-10-30 15:14:56 -040095 const onIncomingAccountMessage = new Subject<IncomingAccountMessage>();
96 handlers.IncomingAccountMessage = (accountId: string, from: string, message: Record<string, string>) =>
97 onIncomingAccountMessage.next({ accountId, from, message });
98
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -040099 const onConversationReady = new Subject<ConversationReady>();
100 handlers.ConversationReady = (accountId: string, conversationId: string) =>
101 onConversationReady.next({ accountId, conversationId });
102
103 const onConversationRemoved = new Subject<ConversationRemoved>();
104 handlers.ConversationRemoved = (accountId: string, conversationId: string) =>
105 onConversationRemoved.next({ accountId, conversationId });
106
107 const onConversationLoaded = new Subject<ConversationLoaded>();
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400108 handlers.ConversationLoaded = (id: number, accountId: string, conversationId: string, messages: Message[]) =>
109 onConversationLoaded.next({ id, accountId, conversationId, messages });
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400110
111 const onMessageReceived = new Subject<MessageReceived>();
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400112 handlers.MessageReceived = (accountId: string, conversationId: string, message: Message) =>
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400113 onMessageReceived.next({ accountId, conversationId, message });
114
115 // Expose all signals in an events object to allow other handlers to subscribe after jamiSwig.init()
Issam E. Maghnif796a092022-10-09 20:25:26 +0000116 this.events = {
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400117 onAccountsChanged: onAccountsChanged.asObservable(),
118 onAccountDetailsChanged: onAccountDetailsChanged.asObservable(),
Issam E. Maghnif796a092022-10-09 20:25:26 +0000119 onVolatileDetailsChanged: onVolatileDetailsChanged.asObservable(),
120 onRegistrationStateChanged: onRegistrationStateChanged.asObservable(),
121 onNameRegistrationEnded: onNameRegistrationEnded.asObservable(),
122 onRegisteredNameFound: onRegisteredNameFound.asObservable(),
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400123 onKnownDevicesChanged: onKnownDevicesChanged.asObservable(),
Charlieb62c6782022-10-30 15:14:56 -0400124 onIncomingAccountMessage: onIncomingAccountMessage.asObservable(),
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400125 onConversationReady: onConversationReady.asObservable(),
126 onConversationRemoved: onConversationRemoved.asObservable(),
127 onConversationLoaded: onConversationLoaded.asObservable(),
128 onMessageReceived: onMessageReceived.asObservable(),
Issam E. Maghnif796a092022-10-09 20:25:26 +0000129 };
130
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400131 this.setupSignalHandlers();
Issam E. Maghnif796a092022-10-09 20:25:26 +0000132
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400133 // RxJS Subjects are used as signal handlers for the following reasons:
134 // 1. You cannot change event handlers after calling jamiSwig.init()
Issam E. Maghnif796a092022-10-09 20:25:26 +0000135 // 2. You cannot specify multiple handlers for the same event
136 // 3. You cannot specify a default handler
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400137 this.jamiSwig.init(handlers);
Issam E. Maghnif796a092022-10-09 20:25:26 +0000138 }
139
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400140 stop(): void {
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400141 this.jamiSwig.fini();
Misha Krieger-Raynauld62a0da92022-10-22 13:46:59 -0400142 }
143
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400144 getVolatileAccountDetails(accountId: string): VolatileDetails {
145 return stringMapToRecord(this.jamiSwig.getVolatileAccountDetails(accountId)) as unknown as VolatileDetails;
Issam E. Maghnif796a092022-10-09 20:25:26 +0000146 }
147
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400148 getAccountDetails(accountId: string): AccountDetails {
149 return stringMapToRecord(this.jamiSwig.getAccountDetails(accountId)) as unknown as AccountDetails;
150 }
Issam E. Maghnif796a092022-10-09 20:25:26 +0000151
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400152 setAccountDetails(accountId: string, accountDetails: AccountDetails): void {
simon43da57b2022-10-26 18:22:22 -0400153 const accountDetailsStringMap: StringMap = new this.jamiSwig.StringMap();
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400154 for (const [key, value] of Object.entries(accountDetails)) {
155 accountDetailsStringMap.set(key, value);
156 }
157 this.jamiSwig.setAccountDetails(accountId, accountDetailsStringMap);
158 }
Issam E. Maghnif796a092022-10-09 20:25:26 +0000159
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400160 async addAccount(details: Map<string, string | number | boolean>): Promise<string> {
simon43da57b2022-10-26 18:22:22 -0400161 const detailsStringMap: StringMap = new this.jamiSwig.StringMap();
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400162
163 detailsStringMap.set('Account.type', 'RING');
164 for (const [key, value] of details.entries()) {
165 detailsStringMap.set('Account.' + key, value.toString());
166 }
167
168 const accountId = this.jamiSwig.addAccount(detailsStringMap);
Issam E. Maghnif796a092022-10-09 20:25:26 +0000169 return firstValueFrom(
170 this.events.onRegistrationStateChanged.pipe(
Misha Krieger-Raynauld6f9c7ae2022-10-28 11:41:45 -0400171 filter((value) => value.accountId === accountId),
Issam E. Maghnif796a092022-10-09 20:25:26 +0000172 // TODO: is it the only state?
Misha Krieger-Raynauld6f9c7ae2022-10-28 11:41:45 -0400173 // TODO: Replace with string enum in common/
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400174 filter((value) => value.state === 'REGISTERED'),
175 map((value) => value.accountId)
Issam E. Maghnif796a092022-10-09 20:25:26 +0000176 )
177 );
178 }
179
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400180 removeAccount(accountId: string): void {
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400181 this.jamiSwig.removeAccount(accountId);
Issam E. Maghnif796a092022-10-09 20:25:26 +0000182 }
183
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400184 getAccountIds(): string[] {
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400185 return stringVectToArray(this.jamiSwig.getAccountList());
Issam E. Maghnif796a092022-10-09 20:25:26 +0000186 }
187
Charlie2bc0d672022-11-04 11:53:44 -0400188 sendAccountTextMessage(from: string, to: string, message: string): void {
Charlieb62c6782022-10-30 15:14:56 -0400189 const messageStringMap: StringMap = new this.jamiSwig.StringMap();
Charlie2bc0d672022-11-04 11:53:44 -0400190 messageStringMap.set('application/json', message);
191 this.jamiSwig.sendAccountTextMessage(from, to, messageStringMap);
Charlieb62c6782022-10-30 15:14:56 -0400192 }
193
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400194 // TODO: Add interface for returned type
Misha Krieger-Raynauld6f9c7ae2022-10-28 11:41:45 -0400195 async lookupUsername(username: string, accountId?: string) {
196 const hasRingNs = this.jamiSwig.lookupName(accountId || '', '', username);
Issam E. Maghnif796a092022-10-09 20:25:26 +0000197 if (!hasRingNs) {
Issam E. Maghnif796a092022-10-09 20:25:26 +0000198 throw new Error('Jami does not have NS');
199 }
Misha Krieger-Raynauld6f9c7ae2022-10-28 11:41:45 -0400200 return firstValueFrom(
201 this.events.onRegisteredNameFound.pipe(
202 filter((value) => value.username === username),
203 map(({ accountId: _, ...response }) => response) // Remove accountId from response
204 )
205 );
206 }
207
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400208 // TODO: Add interface for returned type
Misha Krieger-Raynauld6f9c7ae2022-10-28 11:41:45 -0400209 async lookupAddress(address: string, accountId?: string) {
210 const hasRingNs = this.jamiSwig.lookupAddress(accountId || '', '', address);
211 if (!hasRingNs) {
212 throw new Error('Jami does not have NS');
213 }
214 return firstValueFrom(
215 this.events.onRegisteredNameFound.pipe(
216 filter((value) => value.address === address),
217 map(({ accountId: _, ...response }) => response) // Remove accountId from response
218 )
219 );
Issam E. Maghnif796a092022-10-09 20:25:26 +0000220 }
221
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400222 // TODO: Create enum for state and return that rather than a number
223 async registerUsername(accountId: string, username: string, password: string): Promise<number> {
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400224 const hasRingNs = this.jamiSwig.registerName(accountId, password, username);
225 if (!hasRingNs) {
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400226 throw new Error('Jami does not have NS');
227 }
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400228 return firstValueFrom(
229 this.events.onNameRegistrationEnded.pipe(
230 filter((value) => value.accountId === accountId),
231 map((value) => value.state)
232 )
233 );
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400234 }
235
236 getDevices(accountId: string): Record<string, string> {
237 return stringMapToRecord(this.jamiSwig.getKnownRingDevices(accountId));
238 }
239
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400240 addContact(accountId: string, contactId: string): void {
Misha Krieger-Raynauldbfed1732022-11-01 20:49:35 -0400241 this.jamiSwig.addContact(accountId, contactId);
242 }
243
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400244 removeContact(accountId: string, contactId: string): void {
Misha Krieger-Raynauldbfed1732022-11-01 20:49:35 -0400245 this.jamiSwig.removeContact(accountId, contactId, false);
246 }
247
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400248 blockContact(accountId: string, contactId: string): void {
Misha Krieger-Raynauldbfed1732022-11-01 20:49:35 -0400249 this.jamiSwig.removeContact(accountId, contactId, true);
250 }
251
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400252 // TODO: Replace Record with interface
Misha Krieger-Raynauldbfed1732022-11-01 20:49:35 -0400253 getContacts(accountId: string): Record<string, string>[] {
254 return vectMapToRecordArray(this.jamiSwig.getContacts(accountId));
255 }
256
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400257 // TODO: Replace Record with interface
Misha Krieger-Raynauldbfed1732022-11-01 20:49:35 -0400258 getContactDetails(accountId: string, contactId: string): Record<string, string> {
259 return stringMapToRecord(this.jamiSwig.getContactDetails(accountId, contactId));
260 }
261
Misha Krieger-Raynauld153a1482022-11-05 12:00:41 -0400262 getDefaultModeratorUris(accountId: string): string[] {
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400263 return stringVectToArray(this.jamiSwig.getDefaultModerators(accountId));
264 }
265
Misha Krieger-Raynauld153a1482022-11-05 12:00:41 -0400266 addDefaultModerator(accountId: string, contactId: string): void {
267 this.jamiSwig.setDefaultModerator(accountId, contactId, true);
268 }
269
270 removeDefaultModerator(accountId: string, contactId: string): void {
271 this.jamiSwig.setDefaultModerator(accountId, contactId, false);
272 }
273
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400274 getConversationIds(accountId: string): string[] {
275 return stringVectToArray(this.jamiSwig.getConversations(accountId));
276 }
277
278 // TODO: Replace Record with interface
279 getConversationInfos(accountId: string, conversationId: string): Record<string, string> {
280 return stringMapToRecord(this.jamiSwig.conversationInfos(accountId, conversationId));
281 }
282
283 // TODO: Replace Record with interface
284 getConversationMembers(accountId: string, conversationId: string): Record<string, string>[] {
285 return vectMapToRecordArray(this.jamiSwig.getConversationMembers(accountId, conversationId));
286 }
287
288 async getConversationMessages(accountId: string, conversationId: string, fromMessage?: string): Promise<Message[]> {
289 const requestId = this.jamiSwig.loadConversationMessages(accountId, conversationId, fromMessage || '', 32);
290 return firstValueFrom(
291 this.events.onConversationLoaded.pipe(
292 filter((value) => value.id === requestId),
293 map((value) => value.messages)
294 )
295 );
296 }
297
298 sendConversationMessage(accountId: string, conversationId: string, message: string, replyTo?: string): void {
299 this.jamiSwig.sendMessage(accountId, conversationId, message, replyTo || '');
300 }
301
Misha Krieger-Raynauld7f959332022-11-04 15:12:53 -0400302 getCallIds(accountId: string): string[] {
303 return stringVectToArray(this.jamiSwig.getCallList(accountId));
304 }
305
306 // TODO: Replace Record with interface
307 getCallDetails(accountId: string, callId: string): Record<string, string> {
308 return stringMapToRecord(this.jamiSwig.getCallDetails(accountId, callId));
309 }
310
Misha Krieger-Raynauldb6f1c322022-10-23 20:42:57 -0400311 getAccountIdFromUsername(username: string): string | undefined {
312 return this.usernamesToAccountIds.get(username);
Issam E. Maghnif796a092022-10-09 20:25:26 +0000313 }
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400314
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400315 private setupSignalHandlers(): void {
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400316 this.events.onAccountsChanged.subscribe(() => {
317 log.debug('Received AccountsChanged');
318 });
319
320 this.events.onAccountDetailsChanged.subscribe((signal) => {
321 log.debug('Received AccountsDetailsChanged', JSON.stringify(signal));
322 });
323
324 this.events.onVolatileDetailsChanged.subscribe(({ accountId, details }) => {
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400325 const username = details['Account.registeredName'];
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400326 log.debug(
327 `Received VolatileDetailsChanged: {"accountId":"${accountId}",` +
328 `"details":{"Account.registeredName":"${username}", ...}}`
329 );
330 // Keep map of usernames to account IDs
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400331 if (username) {
332 this.usernamesToAccountIds.set(username, accountId);
333 }
334 });
335
336 this.events.onRegistrationStateChanged.subscribe((signal) => {
337 log.debug('Received RegistrationStateChanged:', JSON.stringify(signal));
338 });
339
340 this.events.onNameRegistrationEnded.subscribe((signal) => {
341 log.debug('Received NameRegistrationEnded:', JSON.stringify(signal));
342 });
343
344 this.events.onRegisteredNameFound.subscribe((signal) => {
345 log.debug('Received RegisteredNameFound:', JSON.stringify(signal));
346 });
347
348 this.events.onKnownDevicesChanged.subscribe(({ accountId }) => {
349 log.debug(`Received KnownDevicesChanged: {"accountId":"${accountId}", ...}`);
350 });
351
352 this.events.onIncomingAccountMessage.subscribe((signal) => {
353 log.debug('Received IncomingAccountMessage:', JSON.stringify(signal));
Charlie2bc0d672022-11-04 11:53:44 -0400354 const message = JSON.parse(signal.message['application/json']);
355
356 if (message === undefined) {
357 log.debug('Undefined account message');
358 return;
359 }
360
361 if (!Object.values(WebSocketMessageType).includes(message.type)) {
362 log.warn(`Unhandled account message type: ${message.type}`);
363 return;
364 }
365
366 message.data.from = signal.from;
367 message.data.to = signal.accountId;
368 log.info(`Sending ${JSON.stringify(message)} to ${signal.accountId}`);
369 this.ws.send(signal.accountId, message);
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400370 });
371
372 this.events.onConversationReady.subscribe((signal) => {
373 log.debug('Received ConversationReady:', JSON.stringify(signal));
374 });
375
376 this.events.onConversationRemoved.subscribe((signal) => {
377 log.debug('Received ConversationRemoved:', JSON.stringify(signal));
378 });
379
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400380 this.events.onConversationLoaded.subscribe(({ id, accountId, conversationId }) => {
381 log.debug(
382 `Received ConversationLoaded: {"id":"${id}","accountId":"${accountId}",` +
383 `"conversationId":"${conversationId}","messages":[...]}`
384 );
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400385 });
386
387 this.events.onMessageReceived.subscribe((signal) => {
388 log.debug('Received MessageReceived:', JSON.stringify(signal));
Charlie6ddaefe2022-11-01 18:36:29 -0400389 // TODO: Send message to client using WS service
Misha Krieger-Raynauld68a9b562022-10-28 19:47:46 -0400390 });
391 }
Issam E. Maghnif796a092022-10-09 20:25:26 +0000392}