diff --git a/src/bot.js b/src/bot.js index 2ed314b..8fa37cd 100644 --- a/src/bot.js +++ b/src/bot.js @@ -33,8 +33,18 @@ class OcrccBot { return new LocalStorage(localStoragePath); } - sendMessage(roomId, msgText) { - return this.client.sendTextMessage(roomId, msgText).catch(err => { + sendTextMessage(roomId, msgText, showToUser=null) { + const content = { + msgtype: "m.text", + body: msgText, + showToUser: showToUser + } + + this.sendMessage(roomId, content) + } + + sendMessage(roomId, content) { + return this.client.sendMessage(roomId, content).catch(err => { switch (err["name"]) { case "UnknownDeviceError": Object.keys(err.devices).forEach(userId => { @@ -42,7 +52,7 @@ class OcrccBot { this.client.setDeviceVerified(userId, deviceId, true); }); }); - return this.sendMessage(roomId, msgText); + return this.sendMessage(roomId, content); break; default: logger.log("error", `ERROR SENDING MESSAGE: ${err}`); @@ -120,20 +130,22 @@ class OcrccBot { this.awaitingFacilitator[roomId] = true; let chatOffline = true; this.client - .getJoinedRoomMembers(process.env.FACILITATOR_ROOM_ID) - .then(members => { + .getGroupUsers(process.env.FACILITATOR_GROUP_ID) + .then(res => { + const members = res.chunk let onlineMembersCount = 0; - Object.keys(members["joined"]).forEach(member => { - const user = this.client.getUser(member); - if (user.presence === "online" && member !== process.env.BOT_USERID) { + members.forEach(member => { + const memberId = member.user_id + const user = this.client.getUser(memberId); + if (user && user.presence === "online" && memberId !== process.env.BOT_USERID) { chatOffline = false; - this.inviteUserToRoom(this.client, roomId, member); + this.inviteUserToRoom(this.client, roomId, memberId); } }); }) .then(() => { if (chatOffline) { - this.sendMessage(roomId, process.env.CHAT_OFFLINE_MESSAGE); + this.sendTextMessage(roomId, process.env.CHAT_OFFLINE_MESSAGE); } }) .catch(err => { @@ -145,11 +157,12 @@ class OcrccBot { uninviteFacilitators(roomId) { this.awaitingFacilitator[roomId] = false; this.client - .getJoinedRoomMembers(process.env.FACILITATOR_ROOM_ID) - .then(allFacilitators => { + .getGroupUsers(process.env.FACILITATOR_GROUP_ID) + .then(groupUsers => { this.client.getJoinedRoomMembers(roomId).then(roomMembers => { const membersIds = Object.keys(roomMembers["joined"]); - const facilitatorsIds = Object.keys(allFacilitators["joined"]); + const facilitators = groupUsers.chunk + const facilitatorsIds = facilitators.map(f => f.user_id); facilitatorsIds.forEach(f => { if (!membersIds.includes(f)) { this.kickUserFromRoom(this.client, roomId, f); @@ -165,15 +178,34 @@ class OcrccBot { handleBotCrash(roomId, error) { if (roomId) { - this.sendMessage(roomId, BOT_ERROR_MESSAGE); + this.sendTextMessage(roomId, BOT_ERROR_MESSAGE); } - this.sendMessage( + this.sendTextMessage( process.env.FACILITATOR_ROOM_ID, `The Help Bot ran into an error: ${error}. Please verify that the chat service is working.` ); } + handleMessageEvent(event) { + const content = event.getContent(); + + // do nothing if there's no content + if (!content) { + return; + } + + // bot commands + if (content.body.startsWith('!bot')) { + return this.handleBotCommand(event); + } + + // write to transcript + if (process.env.CAPTURE_TRANSCRIPTS) { + return this.writeToTranscript(event) + } + } + writeToTranscript(event) { try { const sender = event.getSender(); @@ -184,11 +216,6 @@ class OcrccBot { timeZone: "America/New_York" }); const filepath = this.activeChatrooms[roomId].transcriptFile; - - if (!content) { - return; - } - const message = `${sender} [${time}]: ${content.body}\n`; fs.appendFileSync(filepath, message, "utf8"); @@ -197,6 +224,62 @@ class OcrccBot { } } + handleBotCommand(event) { + try { + const senderId = event.getSender(); + const roomId = event.getRoomId(); + const content = event.getContent(); + const command = content.body.substring("!bot".length).trim() + + switch(command) { + case 'transcript': + this.sendTranscript(senderId, roomId) + break; + case 'transcript please': + this.sendTranscript(senderId, roomId) + break; + case 'hi': + const responses = ['Hi!', 'Hello', 'Hey :)', 'Hi there', 'Bleep bloop'] + const message = responses[Math.floor(Math.random() * responses.length)] + this.sendTextMessage(roomId, message, senderId) + break; + default: + this.sendTextMessage(roomId, `Sorry, I don't know that command. I'm not a very smart bot.`, senderId) + break; + } + } catch (err) { + logger.log("error", `ERROR EXECUTING BOT COMMAND: ${err}`); + } + } + + sendTranscript(senderId, roomId) { + const transcriptFile = this.activeChatrooms[roomId].transcriptFile; + if (!transcriptFile) { + this.sendTextMessage(roomId, 'There is no transcript for this chat.', senderId) + } + + const filename = path.basename(transcriptFile) || 'Transcript'; + const stream = fs.createReadStream(transcriptFile); + + this.client.uploadContent({ + stream: stream, + name: filename + }).then((contentUrl) => { + const content = { + msgtype: "m.file", + body: filename, + url: JSON.parse(contentUrl).content_uri, + showToUser: senderId + }; + + this.sendMessage(roomId, content); + }).catch(err => { + logger.log("error", `ERROR UPLOADING CONTENT: ${err}`) + this.sendTextMessage(roomId, 'There was an error uploading the transcript.', senderId) + }) + + } + deleteOldDevices() { const doDelete = (oldDevices, auth=null, retries=0) => { if (retries > MAX_RETRIES) { @@ -246,35 +329,6 @@ class OcrccBot { }); } - // deleteOldDevices() { - // return this.client.getDevices().then(data => { - // const currentDeviceId = this.client.getDeviceId(); - // const allDeviceIds = data.devices.map(d => d.device_id); - // const oldDevices = allDeviceIds.filter(id => id !== currentDeviceId); - // logger.log("info", `DELETING OLD DEVICES: ${oldDevices}`); - // this.client - // .deleteMultipleDevices(oldDevices) - // .catch(err => { - // const auth = { - // session: err.data.session, - // type: "m.login.password", - // user: process.env.BOT_USERID, - // identifier: { type: "m.id.user", user: process.env.BOT_USERID }, - // password: process.env.BOT_PASSWORD - // }; - // this.client - // .deleteMultipleDevices(oldDevices, auth) - // .then(() => logger.log("info", "DELETED OLD DEVICES")) - // .catch(err => - // logger.log( - // "error", - // `ERROR DELETING OLD DEVICES: ${JSON.stringify(err.data)}` - // ) - // ); - // }); - // }); - // } - setMembershipListeners() { // Automatically accept all room invitations return this.client.on("RoomMember.membership", (event, member) => { @@ -287,7 +341,7 @@ class OcrccBot { this.client .joinRoom(member.roomId) .then(room => { - this.sendMessage( + this.sendTextMessage( process.env.FACILITATOR_ROOM_ID, `A support seeker requested a chat (Room ID: ${member.roomId})` ); @@ -307,11 +361,11 @@ class OcrccBot { this.activeChatrooms[member.roomId] = { facilitator: member.userId }; - this.sendMessage( + this.sendTextMessage( member.roomId, `${member.name} has joined the chat.` ); - this.sendMessage( + this.sendTextMessage( process.env.FACILITATOR_ROOM_ID, `${member.name} joined the chat (Room ID: ${member.roomId})` ); @@ -342,7 +396,7 @@ class OcrccBot { this.activeChatrooms[member.roomId] && member.userId === this.activeChatrooms[member.roomId].facilitator ) { - this.sendMessage( + this.sendTextMessage( member.roomId, `${member.name} has left the chat.` ); @@ -357,7 +411,7 @@ class OcrccBot { return logger.log("error", `ERROR DECRYPTING EVENT: ${err}`); } if (event.getType() === "m.room.message") { - this.writeToTranscript(event); + this.handleMessageEvent(event) } }); // unencrypted messages @@ -369,7 +423,7 @@ class OcrccBot { if (event.isEncrypted()) { return; } - this.writeToTranscript(event); + this.handleMessageEvent(event) } }); } @@ -412,10 +466,7 @@ class OcrccBot { ) .then(() => { this.setMembershipListeners() - - if (process.env.CAPTURE_TRANSCRIPTS) { - this.setMessageListeners() - } + this.setMessageListeners() }) .then(() => this.client.startClient({ initialSyncLimit: 0 })) .catch(err => {