From a25c71a04a3feef154c6981818c9f86a2e819ff6 Mon Sep 17 00:00:00 2001 From: Sharon Kennedy Date: Sun, 6 Sep 2020 14:07:44 -0400 Subject: [PATCH] latest build --- dist/bot.js | 163 ++++++++++++++++++++++++++++++----------------- dist/bot.test.js | 14 ++++ dist/index.js | 10 ++- 3 files changed, 127 insertions(+), 60 deletions(-) diff --git a/dist/bot.js b/dist/bot.js index 4f25e6f..f43f9dc 100644 --- a/dist/bot.js +++ b/dist/bot.js @@ -33,6 +33,7 @@ class OcrccBot { this.config = botConfig; this.client = matrix.createClient(this.config.MATRIX_SERVER_URL); this.joinedRooms = []; + this.inactivityTimers = {}; } createLocalStorage() { @@ -188,10 +189,19 @@ class OcrccBot { } handleMessageEvent(event) { - const content = event.getContent(); // do nothing if there's no content + const content = event.getContent(); + const sender = event.getSender(); + const roomId = event.getRoomId(); // do nothing if there's no content if (!content) { return; + } // if it's a chat message and the facilitator has joined, reset the inactivity timeout + + + const facilitatorId = this.localStorage.getItem(`${roomId}-facilitator`); + + if (Boolean(facilitatorId) && sender !== this.config.BOT_USERID) { + this.setInactivityTimeout(roomId); } // bot commands @@ -434,6 +444,7 @@ class OcrccBot { const notification = `Incoming support chat at ${chatTime} (room ID: ${roomId})`; this.sendTextMessage(this.config.FACILITATOR_ROOM_ID, notification); this.inviteFacilitators(room.roomId); + this.setTimeoutforFacilitator(room.roomId); } } catch (err) { _logger.default.log("error", "ERROR JOINING ROOM => " + err); @@ -441,74 +452,83 @@ class OcrccBot { } if (member.membership === "join" && member.userId !== this.config.BOT_USERID && this.localStorage.getItem(`${member.roomId}-waiting`)) { - // make sure it's a facilitator joining - const roomMembers = await this.client.getJoinedRoomMembers(this.config.FACILITATOR_ROOM_ID); - const members = Object.keys(roomMembers["joined"]); - const isFacilitator = members.includes(member.userId); + try { + // make sure it's a facilitator joining + const roomMembers = await this.client.getJoinedRoomMembers(this.config.FACILITATOR_ROOM_ID); + const members = Object.keys(roomMembers["joined"]); + const isFacilitator = members.includes(member.userId); - if (isFacilitator) { - // made facilitator a moderator in the room - this.localStorage.setItem(`${member.roomId}-facilitator`, member.userId); - const event = { - getType: () => { - return "m.room.power_levels"; - }, - getContent: () => { - return { - users: { - [this.config.BOT_USERID]: 100, - [member.userId]: 50 - } - }; - } - }; - this.client.setPowerLevel(member.roomId, member.userId, 50, event); // send notification to Support Chat Notifications room - - const currentDate = new Date(); - const chatTime = currentDate.toLocaleTimeString(); - const roomId = member.roomId.split(':')[0]; - const notification = `${member.name} joined the chat at ${chatTime} (room ID: ${roomId})`; - this.sendTextMessage(this.config.FACILITATOR_ROOM_ID, notification); // send notification to chat room - - this.sendTextMessage(member.roomId, `${member.name} has joined the chat.`); // revoke the other invitations - - this.uninviteFacilitators(member.roomId); // set transcript file - - if (this.config.CAPTURE_TRANSCRIPTS) { - const currentDate = new Date(); - const dateOpts = { - year: "numeric", - month: "short", - day: "numeric" + if (isFacilitator) { + // made facilitator a moderator in the room + this.localStorage.setItem(`${member.roomId}-facilitator`, member.userId); + const event = { + getType: () => { + return "m.room.power_levels"; + }, + getContent: () => { + return { + users: { + [this.config.BOT_USERID]: 100, + [member.userId]: 50 + } + }; + } }; - const chatDate = currentDate.toLocaleDateString("en-GB", dateOpts); - const chatTime = currentDate.toLocaleTimeString("en-GB", { - timeZone: "America/New_York" - }); - const filename = `${chatDate} - ${chatTime} - ${member.roomId}.txt`; - const filepath = path.resolve(path.join("transcripts", filename)); - this.localStorage.setItem(`${member.roomId}-transcript`, filepath); + this.client.setPowerLevel(member.roomId, member.userId, 50, event); // send notification to Support Chat Notifications room + + const currentDate = new Date(); + const chatTime = currentDate.toLocaleTimeString(); + const roomId = member.roomId.split(':')[0]; + const notification = `${member.name} joined the chat at ${chatTime} (room ID: ${roomId})`; + this.sendTextMessage(this.config.FACILITATOR_ROOM_ID, notification); // send notification to chat room + + this.sendTextMessage(member.roomId, `${member.name} has joined the chat.`); // revoke the other invitations + + this.uninviteFacilitators(member.roomId); // set transcript file + + if (this.config.CAPTURE_TRANSCRIPTS) { + const currentDate = new Date(); + const dateOpts = { + year: "numeric", + month: "short", + day: "numeric" + }; + const chatDate = currentDate.toLocaleDateString("en-GB", dateOpts); + const chatTime = currentDate.toLocaleTimeString("en-GB", { + timeZone: "America/New_York" + }); + const filename = `${chatDate} - ${chatTime} - ${member.roomId}.txt`; + const filepath = path.resolve(path.join("transcripts", filename)); + this.localStorage.setItem(`${member.roomId}-transcript`, filepath); + } } + } catch (err) { + _logger.default.log("error", `ERROR WHEN FACILITATOR JOINED ROOM ==> ${err}`); } } if (member.membership === "leave" && member.userId !== this.config.BOT_USERID) { - // ensure bot is still in the room - const roomData = await this.client.getJoinedRooms(); - const joinedRooms = roomData["joined_rooms"]; - const isBotInRoom = joinedRooms.includes(member.roomId); const room = this.client.getRoom(member.roomId); - if (!room) return; // notify room if the facilitator has left + if (!room) return; + const roomMembers = await room.getJoinedMembers(); // array - const facilitatorId = this.localStorage.getItem(`${member.roomId}-facilitator`); + const facilitatorRoomMembers = await this.client.getJoinedRoomMembers(this.config.FACILITATOR_ROOM_ID); // object - if (isBotInRoom && member.userId === facilitatorId) { - this.sendTextMessage(member.roomId, `${member.name} has left the chat.`); + const isBotInRoom = roomMembers.find(member => member.userId === this.config.BOT_USERID); // notify room if the facilitator has left + + try { + const facilitatorId = this.localStorage.getItem(`${member.roomId}-facilitator`); + + if (isBotInRoom && member.userId === facilitatorId) { + this.sendTextMessage(member.roomId, `${member.name} has left the chat.`); + } + } catch (err) { + _logger.default.log("error", `ERROR NOTIFYING THAT FACLITATOR HAS LEFT THE ROOM ==> ${err}`); } // leave if there is nobody in the room try { - const memberCount = room.getJoinedMemberCount(); + const memberCount = roomMembers.length; if (memberCount === 1 && isBotInRoom) { // just the bot left @@ -525,8 +545,6 @@ class OcrccBot { try { - const roomMembers = await room.getJoinedMembers(); - const facilitatorRoomMembers = await this.client.getJoinedRoomMembers(this.config.FACILITATOR_ROOM_ID); const facilitators = facilitatorRoomMembers['joined']; let facilitatorInRoom = false; roomMembers.forEach(member => { @@ -545,6 +563,30 @@ class OcrccBot { }); } + setTimeoutforFacilitator(roomId) { + setTimeout(() => { + const stillWaiting = this.localStorage.getItem(`${roomId}-waiting`); + + if (stillWaiting) { + this.sendBotSignal(roomId, BOT_SIGNAL_END_CHAT); + } + }, this.config.MAX_WAIT_TIME); + } + + setInactivityTimeout(roomId) { + const oldTimeout = this.inactivityTimers[roomId]; + + if (oldTimeout) { + clearTimeout(oldTimeout); + } + + const newTimeout = setTimeout(() => { + this.sendTextMessage(roomId, `This chat has been closed due to inactivity.`); + this.sendBotSignal(roomId, BOT_SIGNAL_END_CHAT); + }, this.config.MAX_INACTIVE); + this.inactivityTimers[roomId] = newTimeout; + } + async setMessageListeners() { // encrypted messages this.client.on("Event.decrypted", (event, err) => { @@ -580,7 +622,12 @@ class OcrccBot { signal: signal, args: args }; - await this.client.sendStateEvent(roomId, 'm.bot.signal', content); + + try { + await this.client.sendStateEvent(roomId, 'm.bot.signal', content); + } catch (err) { + _logger.default.log('error', "ERROR SENDING BOT SIGNAL => " + err); + } } async start() { diff --git a/dist/bot.test.js b/dist/bot.test.js index 04fe354..a57f6c2 100644 --- a/dist/bot.test.js +++ b/dist/bot.test.js @@ -103,6 +103,8 @@ describe('OcrccBot', () => { mockAppendFileSync.mockClear(); _matrixJsSdk.mockGetGroupUsers.mockClear(); + + _matrixJsSdk.mockSendStateEvent.mockClear(); }); test('constructor should inititialize class variables', () => { const bot = new _bot.default(botConfig); @@ -276,4 +278,16 @@ describe('OcrccBot', () => { expect(_matrixJsSdk.mockStartClient).toHaveBeenCalled(); }); }); + test('#sendBotSignal should send custom state event', () => { + const bot = new _bot.default(botConfig); + bot.start(); + const test_room_id = 'test_room_id'; + const signal = 'END_CHAT'; + bot.sendBotSignal(test_room_id, signal); + (0, _waitForExpect.default)(() => { + expect(_matrixJsSdk.mockSendStateEvent).toHaveBeenCalledWith(test_room_id, 'm.bot.signal', { + signal + }); + }); + }); }); \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index caaf407..89fc8e1 100644 --- a/dist/index.js +++ b/dist/index.js @@ -19,7 +19,10 @@ const { BOT_PASSWORD, BOT_DISPLAY_NAME, FACILITATOR_ROOM_ID, - CAPTURE_TRANSCRIPTS + CAPTURE_TRANSCRIPTS, + CHAT_NOT_AVAILABLE_MESSAGE, + MAX_WAIT_TIME, + MAX_INACTIVE } = process.env; const botConfig = { ENCRYPTION_CONFIG, @@ -32,7 +35,10 @@ const botConfig = { BOT_PASSWORD, BOT_DISPLAY_NAME, FACILITATOR_ROOM_ID, - CAPTURE_TRANSCRIPTS + CAPTURE_TRANSCRIPTS, + CHAT_NOT_AVAILABLE_MESSAGE, + MAX_WAIT_TIME, + MAX_INACTIVE }; const bot = new _bot.default(botConfig); bot.start(); \ No newline at end of file