leave rooms when empty, use localStorage
This commit is contained in:
parent
39d97343a5
commit
aef215bd7e
716
dist/bot.js
vendored
716
dist/bot.js
vendored
@ -1,11 +1,13 @@
|
||||
"use strict";
|
||||
|
||||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
|
||||
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
exports.default = void 0;
|
||||
|
||||
var fs = _interopRequireWildcard(require("fs"));
|
||||
|
||||
@ -21,448 +23,436 @@ var matrix = _interopRequireWildcard(require("matrix-js-sdk"));
|
||||
|
||||
var _logger = _interopRequireDefault(require("./logger"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
|
||||
|
||||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
||||
|
||||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
||||
|
||||
global.Olm = require("olm");
|
||||
var ENCRYPTION_CONFIG = {
|
||||
algorithm: "m.megolm.v1.aes-sha2"
|
||||
};
|
||||
var KICK_REASON = "A facilitator has already joined this chat.";
|
||||
var BOT_ERROR_MESSAGE = "Something went wrong on our end, please restart the chat and try again.";
|
||||
var MAX_RETRIES = 3;
|
||||
|
||||
var OcrccBot =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
function OcrccBot() {
|
||||
_classCallCheck(this, OcrccBot);
|
||||
|
||||
class OcrccBot {
|
||||
constructor(botConfig) {
|
||||
this.config = botConfig;
|
||||
this.awaitingFacilitator = {};
|
||||
this.client = matrix.createClient(process.env.MATRIX_SERVER_URL);
|
||||
this.client = matrix.createClient(this.config.MATRIX_SERVER_URL);
|
||||
this.joinedRooms = [];
|
||||
this.activeChatrooms = {};
|
||||
}
|
||||
|
||||
_createClass(OcrccBot, [{
|
||||
key: "createLocalStorage",
|
||||
value: function createLocalStorage() {
|
||||
var storageLoc = "matrix-chatbot-".concat(process.env.BOT_USERNAME);
|
||||
var dir = path.resolve(path.join(os.homedir(), ".local-storage"));
|
||||
createLocalStorage() {
|
||||
const storageLoc = `matrix-chatbot-${this.config.BOT_USERNAME}`;
|
||||
const dir = path.resolve(path.join(os.homedir(), ".local-storage"));
|
||||
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir);
|
||||
}
|
||||
|
||||
var localStoragePath = path.resolve(path.join(dir, storageLoc));
|
||||
const localStoragePath = path.resolve(path.join(dir, storageLoc));
|
||||
return new _nodeLocalstorage.LocalStorage(localStoragePath);
|
||||
}
|
||||
}, {
|
||||
key: "sendMessage",
|
||||
value: function sendMessage(roomId, msgText) {
|
||||
var _this = this;
|
||||
|
||||
return this.client.sendTextMessage(roomId, msgText)["catch"](function (err) {
|
||||
sendTextMessage(roomId, msgText, showToUser = null) {
|
||||
const content = {
|
||||
msgtype: "m.text",
|
||||
body: msgText,
|
||||
showToUser: showToUser
|
||||
};
|
||||
this.sendMessage(roomId, content);
|
||||
}
|
||||
|
||||
async sendMessage(roomId, content) {
|
||||
_logger.default.log("info", `SENDING MESSAGE: ${content.body}`);
|
||||
|
||||
try {
|
||||
await this.client.sendMessage(roomId, content);
|
||||
} catch (err) {
|
||||
switch (err["name"]) {
|
||||
case "UnknownDeviceError":
|
||||
Object.keys(err.devices).forEach(function (userId) {
|
||||
Object.keys(err.devices[userId]).map(function (deviceId) {
|
||||
_this.client.setDeviceVerified(userId, deviceId, true);
|
||||
});
|
||||
});
|
||||
return _this.sendMessage(roomId, msgText);
|
||||
break;
|
||||
|
||||
default:
|
||||
_logger["default"].log("error", "ERROR SENDING MESSAGE: ".concat(err));
|
||||
|
||||
_this.handleBotCrash(roomId, err);
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "inviteUserToRoom",
|
||||
value: function inviteUserToRoom(client, roomId, member) {
|
||||
var _this2 = this;
|
||||
|
||||
var retries = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
|
||||
|
||||
_logger["default"].log("info", "INVITING MEMBER: " + member);
|
||||
|
||||
if (retries > MAX_RETRIES) {
|
||||
this.handleBotCrash(roomId, "Rate limit exceeded for bot account");
|
||||
return _logger["default"].log("error", "RATE LIMIT EXCEEDED AND RETRY LIMIT EXCEEDED");
|
||||
}
|
||||
|
||||
return client.invite(roomId, member)["catch"](function (err) {
|
||||
switch (err["name"]) {
|
||||
case "M_LIMIT_EXCEEDED":
|
||||
_logger["default"].log("info", "Rate limit exceeded, retrying.");
|
||||
|
||||
var retryCount = retries + 1;
|
||||
var delay = retryCount * 2 * 1000;
|
||||
return setTimeout(_this2.inviteUserToRoom, delay, client, roomId, member, retryCount);
|
||||
break;
|
||||
|
||||
default:
|
||||
_logger["default"].log("error", "ERROR INVITING MEMBER: ".concat(err));
|
||||
|
||||
_this2.handleBotCrash(roomId, err);
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "kickUserFromRoom",
|
||||
value: function kickUserFromRoom(client, roomId, member) {
|
||||
var _this3 = this;
|
||||
|
||||
var retries = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
|
||||
|
||||
_logger["default"].log("info", "KICKING OUT MEMBER: " + member);
|
||||
|
||||
if (retries > MAX_RETRIES) {
|
||||
this.handleBotCrash(roomId, "Rate limit exceeded for bot account.");
|
||||
return _logger["default"].log("error", "RATE LIMIT EXCEEDED AND RETRY LIMIT EXCEEDED");
|
||||
}
|
||||
|
||||
return client.kick(roomId, member, KICK_REASON)["catch"](function (err) {
|
||||
switch (err["name"]) {
|
||||
case "M_LIMIT_EXCEEDED":
|
||||
_logger["default"].log("info", "Rate limit exceeded, retrying.");
|
||||
|
||||
var retryCount = retries + 1;
|
||||
var delay = retryCount * 2 * 1000;
|
||||
return setTimeout(_this3.kickUserFromRoom, delay, client, roomId, member, retryCount);
|
||||
break;
|
||||
|
||||
default:
|
||||
_this3.handleBotCrash(roomId, err);
|
||||
|
||||
_logger["default"].log("error", "ERROR KICKING OUT MEMBER: ".concat(err));
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "inviteFacilitators",
|
||||
value: function inviteFacilitators(roomId) {
|
||||
var _this4 = this;
|
||||
|
||||
this.awaitingFacilitator[roomId] = true;
|
||||
var chatOffline = true;
|
||||
this.client.getJoinedRoomMembers(process.env.FACILITATOR_ROOM_ID).then(function (members) {
|
||||
var onlineMembersCount = 0;
|
||||
Object.keys(members["joined"]).forEach(function (member) {
|
||||
var user = _this4.client.getUser(member);
|
||||
|
||||
if (user.presence === "online" && member !== process.env.BOT_USERID) {
|
||||
chatOffline = false;
|
||||
|
||||
_this4.inviteUserToRoom(_this4.client, roomId, member);
|
||||
}
|
||||
});
|
||||
}).then(function () {
|
||||
if (chatOffline) {
|
||||
_this4.sendMessage(roomId, process.env.CHAT_OFFLINE_MESSAGE);
|
||||
}
|
||||
})["catch"](function (err) {
|
||||
_this4.handleBotCrash(roomId, err);
|
||||
|
||||
_logger["default"].log("error", "ERROR GETTING ROOM MEMBERS: ".concat(err));
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "uninviteFacilitators",
|
||||
value: function uninviteFacilitators(roomId) {
|
||||
var _this5 = this;
|
||||
|
||||
this.awaitingFacilitator[roomId] = false;
|
||||
this.client.getJoinedRoomMembers(process.env.FACILITATOR_ROOM_ID).then(function (allFacilitators) {
|
||||
_this5.client.getJoinedRoomMembers(roomId).then(function (roomMembers) {
|
||||
var membersIds = Object.keys(roomMembers["joined"]);
|
||||
var facilitatorsIds = Object.keys(allFacilitators["joined"]);
|
||||
facilitatorsIds.forEach(function (f) {
|
||||
if (!membersIds.includes(f)) {
|
||||
_this5.kickUserFromRoom(_this5.client, roomId, f);
|
||||
}
|
||||
});
|
||||
});
|
||||
})["catch"](function (err) {
|
||||
_this5.handleBotCrash(roomId, err);
|
||||
|
||||
_logger["default"].log("error", err);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "handleBotCrash",
|
||||
value: function handleBotCrash(roomId, error) {
|
||||
if (roomId) {
|
||||
this.sendMessage(roomId, BOT_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
this.sendMessage(process.env.FACILITATOR_ROOM_ID, "The Help Bot ran into an error: ".concat(error, ". Please verify that the chat service is working."));
|
||||
}
|
||||
}, {
|
||||
key: "writeToTranscript",
|
||||
value: function writeToTranscript(event) {
|
||||
Object.keys(err.devices).forEach(userId => {
|
||||
Object.keys(err.devices[userId]).map(async deviceId => {
|
||||
try {
|
||||
var sender = event.getSender();
|
||||
var roomId = event.getRoomId();
|
||||
var content = event.getContent();
|
||||
var date = event.getDate();
|
||||
var time = date.toLocaleTimeString("en-GB", {
|
||||
timeZone: "America/New_York"
|
||||
await this.client.setDeviceVerified(userId, deviceId, true);
|
||||
} catch (err) {
|
||||
_logger.default.log("error", `ERROR VERIFYING DEVICE: ${err}`);
|
||||
}
|
||||
});
|
||||
var filepath = this.activeChatrooms[roomId].transcriptFile;
|
||||
});
|
||||
await this.sendMessage(roomId, content);
|
||||
|
||||
default:
|
||||
_logger.default.log("error", `ERROR SENDING MESSAGE: ${err}`);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inviteUserToRoom(roomId, member) {
|
||||
try {
|
||||
this.client.invite(roomId, member);
|
||||
} catch (err) {
|
||||
this.handleBotCrash(roomId, err);
|
||||
}
|
||||
}
|
||||
|
||||
kickUserFromRoom(roomId, member) {
|
||||
try {
|
||||
this.client.kick(roomId, member, this.config.KICK_REASON);
|
||||
} catch (err) {
|
||||
this.handleBotCrash(roomId, err);
|
||||
|
||||
_logger.default.log("error", `ERROR KICKING OUT MEMBER: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async inviteFacilitators(roomId) {
|
||||
this.localStorage.setItem(`${roomId}-waiting`, 'true');
|
||||
let chatOffline = true;
|
||||
|
||||
try {
|
||||
const data = await this.client.getGroupUsers(this.config.FACILITATOR_GROUP_ID);
|
||||
const members = data.chunk;
|
||||
members.forEach(member => {
|
||||
const memberId = member.user_id;
|
||||
const user = this.client.getUser(memberId);
|
||||
|
||||
if (user && user.presence === "online" && memberId !== this.config.BOT_USERID) {
|
||||
chatOffline = false;
|
||||
this.inviteUserToRoom(roomId, memberId);
|
||||
}
|
||||
});
|
||||
|
||||
if (chatOffline) {
|
||||
_logger.default.log('info', "NO FACILITATORS ONLINE");
|
||||
|
||||
this.sendTextMessage(roomId, this.config.CHAT_OFFLINE_MESSAGE);
|
||||
}
|
||||
} catch (err) {
|
||||
this.handleBotCrash(roomId, err);
|
||||
|
||||
_logger.default.log("error", `ERROR GETTING FACILITATORS: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async uninviteFacilitators(roomId) {
|
||||
this.localStorage.removeItem(`${roomId}-waiting`);
|
||||
|
||||
try {
|
||||
const groupUsers = await this.client.getGroupUsers(this.config.FACILITATOR_GROUP_ID);
|
||||
const roomMembers = await this.client.getJoinedRoomMembers(roomId);
|
||||
const membersIds = Object.keys(roomMembers["joined"]);
|
||||
const facilitatorsIds = groupUsers.chunk.map(f => f.user_id);
|
||||
facilitatorsIds.forEach(f => {
|
||||
if (!membersIds.includes(f)) {
|
||||
this.kickUserFromRoom(roomId, f);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
this.handleBotCrash(roomId, err);
|
||||
|
||||
_logger.default.log("ERROR UNINVITING FACILITATORS", err);
|
||||
}
|
||||
}
|
||||
|
||||
handleBotCrash(roomId, error) {
|
||||
if (roomId) {
|
||||
this.sendTextMessage(roomId, this.config.BOT_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
this.sendTextMessage(this.config.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 (this.config.CAPTURE_TRANSCRIPTS) {
|
||||
return this.writeToTranscript(event);
|
||||
}
|
||||
}
|
||||
|
||||
var message = "".concat(sender, " [").concat(time, "]: ").concat(content.body, "\n");
|
||||
writeToTranscript(event) {
|
||||
try {
|
||||
const sender = event.getSender();
|
||||
const roomId = event.getRoomId();
|
||||
const content = event.getContent();
|
||||
const date = event.getDate();
|
||||
const time = date.toLocaleTimeString("en-GB", {
|
||||
timeZone: "America/New_York"
|
||||
});
|
||||
const filepath = this.localStorage.getItem(`${roomId}-transcript`);
|
||||
const message = `${sender} [${time}]: ${content.body}\n`;
|
||||
fs.appendFileSync(filepath, message, "utf8");
|
||||
} catch (err) {
|
||||
_logger["default"].log("error", "ERROR APPENDING TO TRANSCRIPT FILE: ".concat(err));
|
||||
_logger.default.log("error", `ERROR APPENDING TO TRANSCRIPT FILE: ${err}`);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "deleteOldDevices",
|
||||
value: function deleteOldDevices() {
|
||||
var _this6 = this;
|
||||
|
||||
var doDelete = function doDelete(oldDevices) {
|
||||
var auth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
||||
var retries = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
||||
handleBotCommand(event) {
|
||||
try {
|
||||
const senderId = event.getSender();
|
||||
const roomId = event.getRoomId();
|
||||
const content = event.getContent();
|
||||
const command = content.body.substring("!bot".length).trim();
|
||||
|
||||
if (retries > MAX_RETRIES) {
|
||||
throw new Error("Exceeded max retries deleting old devices");
|
||||
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.default.log("error", `ERROR EXECUTING BOT COMMAND: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
_logger["default"].log("info", "ATTEMPTING TO DELETE OLD DEVICES: ".concat(oldDevices));
|
||||
async leaveEmptyRooms(senderId) {
|
||||
try {
|
||||
const roomData = await this.client.getJoinedRooms();
|
||||
const joinedRoomsIds = roomData["joined_rooms"];
|
||||
joinedRoomsIds.forEach(async roomId => {
|
||||
const room = this.client.getRoom(roomId);
|
||||
|
||||
_this6.client.deleteMultipleDevices(oldDevices, auth).then(function () {
|
||||
return _logger["default"].log("info", "DELETED OLD DEVICES");
|
||||
})["catch"](function (err) {
|
||||
if (err['errcode'] === undefined && err['data']) {
|
||||
var _auth = {
|
||||
if (room && room.getJoinedMemberCount() === 1) {
|
||||
try {
|
||||
_logger.default.log('info', "LEAVING EMPTY ROOM => " + roomId);
|
||||
|
||||
await this.client.leave(roomId);
|
||||
} catch (err) {
|
||||
_logger.default.log('error', "ERROR LEAVING EMPTY ROOM => " + err);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
_logger.default.log("error", `ERROR GETTING JOINED ROOMS: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async sendTranscript(senderId, roomId) {
|
||||
try {
|
||||
const transcriptFile = this.localStorage.getItem(`${roomId}-transcript`);
|
||||
|
||||
if (!transcriptFile) {
|
||||
this.sendTextMessage(roomId, "There is no transcript for this chat.", senderId);
|
||||
}
|
||||
|
||||
const filename = path.basename(transcriptFile) || "Transcript";
|
||||
const stream = fs.createReadStream(transcriptFile);
|
||||
const contentUrl = await this.client.uploadContent({
|
||||
stream: stream,
|
||||
name: filename
|
||||
});
|
||||
const content = {
|
||||
msgtype: "m.file",
|
||||
body: filename,
|
||||
url: JSON.parse(contentUrl).content_uri,
|
||||
showToUser: senderId
|
||||
};
|
||||
this.sendMessage(roomId, content);
|
||||
} catch (err) {
|
||||
_logger.default.log("error", `ERROR UPLOADING CONTENT: ${err}`);
|
||||
|
||||
this.sendTextMessage(roomId, "There was an error uploading the transcript.", senderId);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteOldDevices() {
|
||||
const currentDeviceId = this.client.getDeviceId();
|
||||
const deviceData = await this.client.getDevices();
|
||||
const allDeviceIds = deviceData.devices.map(d => d.device_id);
|
||||
const oldDevices = allDeviceIds.filter(id => id !== currentDeviceId);
|
||||
|
||||
try {
|
||||
await this.client.deleteMultipleDevices(oldDevices);
|
||||
} catch (err) {
|
||||
_logger.default.log("info", "RETRYING DELETE OLD DEVICES WITH AUTH");
|
||||
|
||||
const auth = {
|
||||
session: err.data.session,
|
||||
type: "m.login.password",
|
||||
user: process.env.BOT_USERID,
|
||||
user: this.config.BOT_USERID,
|
||||
identifier: {
|
||||
type: "m.id.user",
|
||||
user: process.env.BOT_USERID
|
||||
user: this.config.BOT_USERID
|
||||
},
|
||||
password: process.env.BOT_PASSWORD
|
||||
password: this.config.BOT_PASSWORD
|
||||
};
|
||||
doDelete(oldDevices, _auth);
|
||||
} else if (err['errcode'] === 'M_LIMIT_EXCEEDED') {
|
||||
var retryCount = retries + 1;
|
||||
var delay = err['retry_after_ms'] ? err['retry_after_ms'] : retryCount * 1000;
|
||||
await this.client.deleteMultipleDevices(oldDevices, auth);
|
||||
|
||||
_logger["default"].log("error", "RATE LIMIT EXCEEDED, RETRYING IN ".concat(delay, " MS"));
|
||||
|
||||
setTimeout(function () {
|
||||
doDelete(oldDevices, auth, retryCount);
|
||||
}, delay);
|
||||
} else {
|
||||
_logger["default"].log("error", "ERROR DELETING OLD DEVICES ON RETRY ".concat(retries, ": ").concat(JSON.stringify(err)));
|
||||
|
||||
doDelete(oldDevices, auth, retries + 1);
|
||||
_logger.default.log("info", "DELETED OLD DEVICES");
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return this.client.getDevices().then(function (data) {
|
||||
var currentDeviceId = _this6.client.getDeviceId();
|
||||
async trackJoinedRooms() {
|
||||
const roomData = await this.client.getJoinedRooms();
|
||||
this.joinedRooms = roomData["joined_rooms"];
|
||||
|
||||
var allDeviceIds = data.devices.map(function (d) {
|
||||
return d.device_id;
|
||||
});
|
||||
var oldDevices = allDeviceIds.filter(function (id) {
|
||||
return id !== currentDeviceId;
|
||||
});
|
||||
doDelete(oldDevices);
|
||||
})["catch"](function (err) {
|
||||
_this6.handleBotCrash(undefined, err);
|
||||
|
||||
_logger["default"].log("error", "ERROR DELETING OLD DEVICES: ".concat(JSON.stringify(err.data)));
|
||||
});
|
||||
} // 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)}`
|
||||
// )
|
||||
// );
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
}, {
|
||||
key: "setMembershipListeners",
|
||||
value: function setMembershipListeners() {
|
||||
var _this7 = this;
|
||||
_logger.default.log("info", "JOINED ROOMS => " + this.joinedRooms);
|
||||
}
|
||||
|
||||
async setMembershipListeners() {
|
||||
// Automatically accept all room invitations
|
||||
return this.client.on("RoomMember.membership", function (event, member) {
|
||||
if (member.membership === "invite" && member.userId === process.env.BOT_USERID && !_this7.joinedRooms.includes(member.roomId)) {
|
||||
_logger["default"].log("info", "Auto-joining room " + member.roomId);
|
||||
this.client.on("RoomMember.membership", async (event, member) => {
|
||||
if (member.membership === "invite" && member.userId === this.config.BOT_USERID && !this.joinedRooms.includes(member.roomId)) {
|
||||
try {
|
||||
const room = await this.client.joinRoom(member.roomId);
|
||||
|
||||
_this7.client.joinRoom(member.roomId).then(function (room) {
|
||||
_this7.sendMessage(process.env.FACILITATOR_ROOM_ID, "A support seeker requested a chat (Room ID: ".concat(member.roomId, ")"));
|
||||
}).then(function () {
|
||||
return _this7.inviteFacilitators(member.roomId);
|
||||
})["catch"](function (err) {
|
||||
_logger["default"].log("error", err);
|
||||
});
|
||||
} // When a facilitator joins a support session, revoke the other invitations
|
||||
_logger.default.log("info", "AUTO JOINED ROOM => " + room.roomId);
|
||||
|
||||
this.sendTextMessage(this.config.FACILITATOR_ROOM_ID, `A support seeker requested a chat (Room ID: ${room.roomId})`);
|
||||
this.inviteFacilitators(room.roomId);
|
||||
} catch (err) {
|
||||
_logger.default.log("error", "ERROR JOINING ROOM => " + err);
|
||||
}
|
||||
} // When a facilitator joins a support session, make them a moderator
|
||||
// revoke the other invitations
|
||||
|
||||
|
||||
if (member.membership === "join" && member.userId !== process.env.BOT_USERID && _this7.awaitingFacilitator[member.roomId]) {
|
||||
_this7.activeChatrooms[member.roomId] = {
|
||||
facilitator: member.userId
|
||||
if (member.membership === "join" && member.userId !== this.config.BOT_USERID && this.localStorage.getItem(`${member.roomId}-waiting`)) {
|
||||
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);
|
||||
this.sendTextMessage(member.roomId, `${member.name} has joined the chat.`);
|
||||
this.sendTextMessage(this.config.FACILITATOR_ROOM_ID, `${member.name} joined the chat (Room ID: ${member.roomId})`);
|
||||
this.uninviteFacilitators(member.roomId);
|
||||
|
||||
_this7.sendMessage(member.roomId, "".concat(member.name, " has joined the chat."));
|
||||
|
||||
_this7.sendMessage(process.env.FACILITATOR_ROOM_ID, "".concat(member.name, " joined the chat (Room ID: ").concat(member.roomId, ")"));
|
||||
|
||||
_this7.uninviteFacilitators(member.roomId);
|
||||
|
||||
if (process.env.CAPTURE_TRANSCRIPTS) {
|
||||
var currentDate = new Date();
|
||||
var dateOpts = {
|
||||
if (this.config.CAPTURE_TRANSCRIPTS) {
|
||||
const currentDate = new Date();
|
||||
const dateOpts = {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric"
|
||||
};
|
||||
var chatDate = currentDate.toLocaleDateString("en-GB", dateOpts);
|
||||
var chatTime = currentDate.toLocaleTimeString("en-GB", {
|
||||
const chatDate = currentDate.toLocaleDateString("en-GB", dateOpts);
|
||||
const chatTime = currentDate.toLocaleTimeString("en-GB", {
|
||||
timeZone: "America/New_York"
|
||||
});
|
||||
var filename = "".concat(chatDate, " - ").concat(chatTime, " - ").concat(member.roomId, ".txt");
|
||||
var filepath = path.resolve(path.join("transcripts", filename));
|
||||
_this7.activeChatrooms[member.roomId].transcriptFile = filepath;
|
||||
const filename = `${chatDate} - ${chatTime} - ${member.roomId}.txt`;
|
||||
const filepath = path.resolve(path.join("transcripts", filename));
|
||||
this.localStorage.setItem(`${member.roomId}-transcript`, filepath);
|
||||
}
|
||||
}
|
||||
|
||||
if (member.membership === "leave" && member.userId !== process.env.BOT_USERID && _this7.activeChatrooms[member.roomId] && member.userId === _this7.activeChatrooms[member.roomId].facilitator) {
|
||||
_this7.sendMessage(member.roomId, "".concat(member.name, " has left the chat."));
|
||||
if (member.membership === "leave" && member.userId !== this.config.BOT_USERID) {
|
||||
const facilitatorId = this.localStorage.getItem(`${member.roomId}-facilitator`);
|
||||
|
||||
if (member.userId === facilitatorId) {
|
||||
this.sendTextMessage(member.roomId, `${member.name} has left the chat.`);
|
||||
} // leave if there is nobody in the room
|
||||
|
||||
|
||||
const room = this.client.getRoom(member.roomId);
|
||||
if (!room) return;
|
||||
const memberCount = room.getJoinedMemberCount();
|
||||
|
||||
if (memberCount === 1) {
|
||||
// just the bot
|
||||
_logger.default.log("info", `LEAVING EMPTY ROOM ==> ${member.roomId}`);
|
||||
|
||||
this.client.leave(member.roomId);
|
||||
this.localStorage.removeItem(`${member.roomId}-facilitator`);
|
||||
this.localStorage.removeItem(`${member.roomId}-transcript`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "setMessageListeners",
|
||||
value: function setMessageListeners() {
|
||||
var _this8 = this;
|
||||
|
||||
async setMessageListeners() {
|
||||
// encrypted messages
|
||||
this.client.on("Event.decrypted", function (event, err) {
|
||||
this.client.on("Event.decrypted", (event, err) => {
|
||||
if (err) {
|
||||
return _logger["default"].log("error", "ERROR DECRYPTING EVENT: ".concat(err));
|
||||
return _logger.default.log("error", `ERROR DECRYPTING EVENT: ${err}`);
|
||||
}
|
||||
|
||||
if (event.getType() === "m.room.message") {
|
||||
_this8.writeToTranscript(event);
|
||||
this.handleMessageEvent(event);
|
||||
}
|
||||
}); // unencrypted messages
|
||||
|
||||
this.client.on("Room.timeline", function (event, room, toStartOfTimeline) {
|
||||
if (event.getType() === "m.room.message" && !_this8.client.isCryptoEnabled()) {
|
||||
if (event.isEncrypted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_this8.writeToTranscript(event);
|
||||
this.client.on("Room.timeline", (event, room, toStartOfTimeline) => {
|
||||
if (event.getType() === "m.room.message" && !event.isEncrypted()) {
|
||||
this.handleMessageEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "start",
|
||||
value: function start() {
|
||||
var _this9 = this;
|
||||
|
||||
var localStorage = this.createLocalStorage();
|
||||
this.client.login("m.login.password", {
|
||||
user: process.env.BOT_USERNAME,
|
||||
password: process.env.BOT_PASSWORD,
|
||||
initial_device_display_name: process.env.BOT_DISPLAY_NAME
|
||||
}).then(function (data) {
|
||||
var accessToken = data.access_token;
|
||||
var deviceId = data.device_id; // create new client with full options
|
||||
async leaveOldRooms() {
|
||||
const roomData = await this.client.getJoinedRooms();
|
||||
roomData["joined_rooms"].forEach(async roomId => {
|
||||
try {
|
||||
await this.client.leave(roomId);
|
||||
} catch (err) {
|
||||
_logger.default.log('error', "ERROR LEAVING ROOM => " + err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var opts = {
|
||||
baseUrl: process.env.MATRIX_SERVER_URL,
|
||||
accessToken: accessToken,
|
||||
userId: process.env.BOT_USERID,
|
||||
deviceId: deviceId,
|
||||
async start() {
|
||||
const localStorage = this.createLocalStorage();
|
||||
this.localStorage = localStorage;
|
||||
|
||||
try {
|
||||
const auth = {
|
||||
user: this.config.BOT_USERNAME,
|
||||
password: this.config.BOT_PASSWORD,
|
||||
initial_device_display_name: this.config.BOT_DISPLAY_NAME
|
||||
};
|
||||
const account = await this.client.login("m.login.password", auth);
|
||||
|
||||
_logger.default.log("info", `ACCOUNT ==> ${JSON.stringify(account)}`);
|
||||
|
||||
let opts = {
|
||||
baseUrl: this.config.MATRIX_SERVER_URL,
|
||||
accessToken: account.access_token,
|
||||
userId: this.config.BOT_USERID,
|
||||
deviceId: account.device_id,
|
||||
sessionStore: new matrix.WebStorageSessionStore(localStorage)
|
||||
};
|
||||
_this9.client = matrix.createClient(opts);
|
||||
})["catch"](function (err) {
|
||||
_logger["default"].log("error", "ERROR WITH LOGIN: ".concat(err));
|
||||
}).then(function () {
|
||||
return _this9.deleteOldDevices();
|
||||
}).then(function () {
|
||||
return _this9.client.initCrypto();
|
||||
})["catch"](function (err) {
|
||||
return _logger["default"].log("error", "ERROR STARTING CRYPTO: ".concat(err));
|
||||
}).then(function () {
|
||||
return _this9.client.getJoinedRooms().then(function (data) {
|
||||
_this9.joinedRooms = data["joined_rooms"];
|
||||
});
|
||||
}).then(function () {
|
||||
_this9.setMembershipListeners();
|
||||
|
||||
if (process.env.CAPTURE_TRANSCRIPTS) {
|
||||
_this9.setMessageListeners();
|
||||
}
|
||||
}).then(function () {
|
||||
return _this9.client.startClient({
|
||||
this.client = matrix.createClient(opts);
|
||||
await this.deleteOldDevices();
|
||||
await this.trackJoinedRooms();
|
||||
await this.client.initCrypto();
|
||||
await this.setMembershipListeners();
|
||||
await this.setMessageListeners();
|
||||
this.client.startClient({
|
||||
initialSyncLimit: 0
|
||||
});
|
||||
})["catch"](function (err) {
|
||||
_this9.handleBotCrash(undefined, err);
|
||||
} catch (err) {
|
||||
this.handleBotCrash(undefined, err);
|
||||
|
||||
_logger["default"].log("error", "ERROR INITIALIZING CLIENT: ".concat(err));
|
||||
});
|
||||
_logger.default.log("error", `ERROR INITIALIZING CLIENT: ${err}`);
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
return OcrccBot;
|
||||
}();
|
||||
}
|
||||
|
||||
var _default = OcrccBot;
|
||||
exports["default"] = _default;
|
||||
exports.default = _default;
|
253
dist/bot.test.js
vendored
Normal file
253
dist/bot.test.js
vendored
Normal file
@ -0,0 +1,253 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
|
||||
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
|
||||
|
||||
var path = _interopRequireWildcard(require("path"));
|
||||
|
||||
var os = _interopRequireWildcard(require("os"));
|
||||
|
||||
var fs = _interopRequireWildcard(require("fs"));
|
||||
|
||||
var _waitForExpect = _interopRequireDefault(require("wait-for-expect"));
|
||||
|
||||
var _matrixJsSdk = require("matrix-js-sdk");
|
||||
|
||||
var _bot = _interopRequireDefault(require("./bot"));
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const mockAppendFileSync = jest.fn();
|
||||
fs.appendFileSync = mockAppendFileSync;
|
||||
describe('OcrccBot', () => {
|
||||
beforeEach(() => {
|
||||
_matrixJsSdk.createClient.mockClear();
|
||||
|
||||
_matrixJsSdk.mockInitCrypto.mockClear();
|
||||
|
||||
_matrixJsSdk.mockStartClient.mockClear();
|
||||
|
||||
_matrixJsSdk.mockRegisterRequest.mockClear();
|
||||
|
||||
_matrixJsSdk.mockSetPowerLevel.mockClear();
|
||||
|
||||
_matrixJsSdk.mockCreateRoom.mockClear();
|
||||
|
||||
_matrixJsSdk.mockLeave.mockClear();
|
||||
|
||||
_matrixJsSdk.mockDeactivateAccount.mockClear();
|
||||
|
||||
_matrixJsSdk.mockStopClient.mockClear();
|
||||
|
||||
_matrixJsSdk.mockClearStores.mockClear();
|
||||
|
||||
_matrixJsSdk.mockOnce.mockClear();
|
||||
|
||||
_matrixJsSdk.mockOn.mockClear();
|
||||
|
||||
_matrixJsSdk.mockLogin.mockClear();
|
||||
|
||||
_matrixJsSdk.mockGetDevices.mockClear();
|
||||
|
||||
_matrixJsSdk.mockGetDeviceId.mockClear();
|
||||
|
||||
_matrixJsSdk.mockDeleteMultipleDevices.mockClear();
|
||||
|
||||
_matrixJsSdk.mockGetJoinedRooms.mockClear();
|
||||
|
||||
_matrixJsSdk.mockSetDeviceVerified.mockClear();
|
||||
|
||||
_matrixJsSdk.mockInvite.mockClear();
|
||||
|
||||
_matrixJsSdk.mockKick.mockClear();
|
||||
|
||||
_matrixJsSdk.mockGetJoinedRoomMembers.mockClear();
|
||||
|
||||
_matrixJsSdk.mockGetUser.mockClear();
|
||||
|
||||
_matrixJsSdk.mockSendMessage.mockClear();
|
||||
|
||||
_matrixJsSdk.mockSendTextMessage.mockClear();
|
||||
|
||||
mockAppendFileSync.mockClear();
|
||||
|
||||
_matrixJsSdk.mockGetGroupUsers.mockClear();
|
||||
});
|
||||
test('constructor should inititialize class variables', () => {
|
||||
const bot = new _bot.default();
|
||||
expect(bot.joinedRooms).toEqual([]);
|
||||
expect(bot.awaitingFacilitator).toEqual({});
|
||||
expect(bot.activeChatrooms).toEqual({});
|
||||
});
|
||||
test('#createLocalStorage should have correct storage location', () => {
|
||||
const bot = new _bot.default();
|
||||
const localStorage = bot.createLocalStorage();
|
||||
const localStoragePath = path.resolve(path.join(os.homedir(), ".local-storage", `matrix-chatbot-${process.env.BOT_USERNAME}`));
|
||||
expect(localStorage._location).toBe(localStoragePath);
|
||||
});
|
||||
test('#sendMessage should send a text message', () => {
|
||||
const bot = new _bot.default();
|
||||
bot.start();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockStartClient).toHaveBeenCalled();
|
||||
});
|
||||
const testRoom = 'room_id_1234';
|
||||
const testMsg = 'test message';
|
||||
bot.sendMessage(testRoom, testMsg);
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockSetDeviceVerified).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockSendMessage).toHaveBeenCalledWith(testRoom, testMsg);
|
||||
});
|
||||
});
|
||||
test('#inviteUserToRoom should add member to room and retry on rate limit error', () => {
|
||||
const bot = new _bot.default();
|
||||
bot.start();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockStartClient).toHaveBeenCalled();
|
||||
});
|
||||
bot.inviteUserToRoom(bot.client, 'room_id_1234', process.env.BOT_USERNAME);
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockInvite).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
test('#kickUserFromRoom should remove member from room and retry on rate limit error', () => {
|
||||
const bot = new _bot.default();
|
||||
bot.start();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockStartClient).toHaveBeenCalled();
|
||||
});
|
||||
bot.kickUserFromRoom(bot.client, 'room_id_1234', process.env.BOT_USERNAME);
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockKick).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
test('#inviteFacilitators should invite all members from Facilitator room', () => {
|
||||
const bot = new _bot.default();
|
||||
bot.start();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockStartClient).toHaveBeenCalled();
|
||||
});
|
||||
bot.inviteFacilitators();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockGetJoinedRoomMembers).toHaveBeenCalledWith(process.env.FACILITATOR_ROOM_ID);
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockGetUser).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockInvite).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
test('#uninviteFacilitators should remove all members that have not accepted the invite', () => {
|
||||
const bot = new _bot.default();
|
||||
bot.start();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockStartClient).toHaveBeenCalled();
|
||||
});
|
||||
bot.uninviteFacilitators();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockGetJoinedRoomMembers).toHaveBeenCalledWith(process.env.FACILITATOR_ROOM_ID);
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockGetJoinedRoomMembers).toHaveBeenCalledWith('room_id_1234');
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockKick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
test('#handleBotCrash should notify rooms', () => {
|
||||
const bot = new _bot.default();
|
||||
bot.start();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockStartClient).toHaveBeenCalled();
|
||||
});
|
||||
bot.handleBotCrash('test_room_id', 'test error message');
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockSendTextMessage).toHaveBeenCalledWith('test_room_id', "Something went wrong on our end, please restart the chat and try again.");
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockSendTextMessage).toHaveBeenCalledWith(process.env.FACILITATOR_ROOM_ID, `The Help Bot ran into an error: test error message. Please verify that the chat service is working.`);
|
||||
});
|
||||
});
|
||||
test('#writeToTranscript should parse event and write to transcript file', () => {
|
||||
const bot = new _bot.default();
|
||||
bot.start();
|
||||
bot.activeChatrooms['test_room_id'] = {
|
||||
transcriptFile: '__mocks__/test_transcript.txt'
|
||||
};
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockStartClient).toHaveBeenCalled();
|
||||
});
|
||||
const mockEvent = {
|
||||
getSender: () => 'test_sender',
|
||||
getRoomId: () => 'test_room_id',
|
||||
getContent: () => {
|
||||
return {
|
||||
body: 'test content'
|
||||
};
|
||||
},
|
||||
getDate: () => {
|
||||
return new Date(2020, 2, 17, 0, 0, 0, 0);
|
||||
}
|
||||
};
|
||||
bot.writeToTranscript(mockEvent);
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(mockAppendFileSync).toHaveBeenCalledWith('__mocks__/test_transcript.txt', 'test_sender [00:00:00]: test content', 'utf8');
|
||||
});
|
||||
});
|
||||
test('#deleteOldDevices should delete old sessions', () => {
|
||||
const bot = new _bot.default();
|
||||
bot.start();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockStartClient).toHaveBeenCalled();
|
||||
});
|
||||
bot.deleteOldDevices();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockGetDevices).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(mockGetDevicdId).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(deleteMultipleDevices).toHaveBeenCalled();
|
||||
});
|
||||
}); // TODO test listeners for membership events and message events
|
||||
|
||||
test('#start should start bot and set up listeners', () => {
|
||||
const bot = new _bot.default();
|
||||
bot.start();
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockLogin).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.WebStorageSessionStore).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.createClient).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockGetDevices).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockGetDeviceId).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockDeleteMultipleDevices).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockInitCrypto).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockGetJoinedRooms).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockOn).toHaveBeenCalled();
|
||||
});
|
||||
(0, _waitForExpect.default)(() => {
|
||||
expect(_matrixJsSdk.mockStartClient).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
38
dist/index.js
vendored
38
dist/index.js
vendored
@ -1,10 +1,42 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
|
||||
var _bot = _interopRequireDefault(require("./bot"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
var bot = new _bot["default"]();
|
||||
const ENCRYPTION_CONFIG = {
|
||||
algorithm: "m.megolm.v1.aes-sha2"
|
||||
};
|
||||
const KICK_REASON = "A facilitator has already joined this chat.";
|
||||
const BOT_ERROR_MESSAGE = "Something went wrong on our end, please restart the chat and try again.";
|
||||
const MAX_RETRIES = 3;
|
||||
const {
|
||||
MATRIX_SERVER_URL,
|
||||
BOT_USERNAME,
|
||||
BOT_USERID,
|
||||
BOT_PASSWORD,
|
||||
BOT_DISPLAY_NAME,
|
||||
FACILITATOR_GROUP_ID,
|
||||
FACILITATOR_ROOM_ID,
|
||||
CHAT_OFFLINE_MESSAGE,
|
||||
CAPTURE_TRANSCRIPTS
|
||||
} = process.env;
|
||||
const botConfig = {
|
||||
ENCRYPTION_CONFIG,
|
||||
KICK_REASON,
|
||||
BOT_ERROR_MESSAGE,
|
||||
MAX_RETRIES,
|
||||
MATRIX_SERVER_URL,
|
||||
BOT_USERNAME,
|
||||
BOT_USERID,
|
||||
BOT_PASSWORD,
|
||||
BOT_DISPLAY_NAME,
|
||||
FACILITATOR_GROUP_ID,
|
||||
FACILITATOR_ROOM_ID,
|
||||
CHAT_OFFLINE_MESSAGE,
|
||||
CAPTURE_TRANSCRIPTS
|
||||
};
|
||||
const bot = new _bot.default(botConfig);
|
||||
bot.start();
|
20
dist/logger.js
vendored
20
dist/logger.js
vendored
@ -1,17 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
exports.default = void 0;
|
||||
|
||||
var _winston = _interopRequireDefault(require("winston"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
var logger = _winston["default"].createLogger({
|
||||
const logger = _winston.default.createLogger({
|
||||
level: "info",
|
||||
format: _winston["default"].format.json(),
|
||||
format: _winston.default.format.json(),
|
||||
defaultMeta: {
|
||||
service: "user-service"
|
||||
},
|
||||
@ -19,10 +19,10 @@ var logger = _winston["default"].createLogger({
|
||||
// - Write all logs with level `error` and below to `error.log`
|
||||
// - Write all logs with level `info` and below to `combined.log`
|
||||
//
|
||||
new _winston["default"].transports.File({
|
||||
new _winston.default.transports.File({
|
||||
filename: "error.log",
|
||||
level: "error"
|
||||
}), new _winston["default"].transports.File({
|
||||
}), new _winston.default.transports.File({
|
||||
filename: "combined.log"
|
||||
})]
|
||||
}); //
|
||||
@ -32,10 +32,10 @@ var logger = _winston["default"].createLogger({
|
||||
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
logger.add(new _winston["default"].transports.Console({
|
||||
format: _winston["default"].format.simple()
|
||||
logger.add(new _winston.default.transports.Console({
|
||||
format: _winston.default.format.simple()
|
||||
}));
|
||||
}
|
||||
|
||||
var _default = logger;
|
||||
exports["default"] = _default;
|
||||
exports.default = _default;
|
43
src/bot.js
43
src/bot.js
@ -17,7 +17,6 @@ class OcrccBot {
|
||||
this.awaitingFacilitator = {};
|
||||
this.client = matrix.createClient(this.config.MATRIX_SERVER_URL);
|
||||
this.joinedRooms = [];
|
||||
this.activeChatrooms = {};
|
||||
}
|
||||
|
||||
createLocalStorage() {
|
||||
@ -56,7 +55,7 @@ class OcrccBot {
|
||||
}
|
||||
});
|
||||
});
|
||||
return this.sendMessage(roomId, content);
|
||||
await this.sendMessage(roomId, content);
|
||||
default:
|
||||
logger.log("error", `ERROR SENDING MESSAGE: ${err}`);
|
||||
break;
|
||||
@ -82,7 +81,7 @@ class OcrccBot {
|
||||
}
|
||||
|
||||
async inviteFacilitators(roomId) {
|
||||
this.awaitingFacilitator[roomId] = true;
|
||||
this.localStorage.setItem(`${roomId}-waiting`, 'true')
|
||||
let chatOffline = true;
|
||||
|
||||
try {
|
||||
@ -114,7 +113,7 @@ class OcrccBot {
|
||||
}
|
||||
|
||||
async uninviteFacilitators(roomId) {
|
||||
this.awaitingFacilitator[roomId] = false;
|
||||
this.localStorage.removeItem(`${roomId}-waiting`)
|
||||
|
||||
try {
|
||||
const groupUsers = await this.client.getGroupUsers(this.config.FACILITATOR_GROUP_ID)
|
||||
@ -173,7 +172,7 @@ class OcrccBot {
|
||||
const time = date.toLocaleTimeString("en-GB", {
|
||||
timeZone: "America/New_York"
|
||||
});
|
||||
const filepath = this.activeChatrooms[roomId].transcriptFile;
|
||||
const filepath = this.localStorage.getItem(`${roomId}-transcript`)
|
||||
const message = `${sender} [${time}]: ${content.body}\n`;
|
||||
|
||||
fs.appendFileSync(filepath, message, "utf8");
|
||||
@ -190,9 +189,6 @@ class OcrccBot {
|
||||
const command = content.body.substring("!bot".length).trim();
|
||||
|
||||
switch (command) {
|
||||
case "purge rooms":
|
||||
this.leaveEmptyRooms(senderId);
|
||||
break;
|
||||
case "transcript":
|
||||
this.sendTranscript(senderId, roomId);
|
||||
break;
|
||||
@ -246,9 +242,8 @@ class OcrccBot {
|
||||
|
||||
async sendTranscript(senderId, roomId) {
|
||||
try {
|
||||
const transcriptFile = this.activeChatrooms[roomId]
|
||||
? this.activeChatrooms[roomId].transcriptFile
|
||||
: false;
|
||||
const transcriptFile = this.localStorage.getItem(`${roomId}-transcript`)
|
||||
|
||||
if (!transcriptFile) {
|
||||
this.sendTextMessage(
|
||||
roomId,
|
||||
@ -320,17 +315,16 @@ class OcrccBot {
|
||||
member.userId === this.config.BOT_USERID &&
|
||||
!this.joinedRooms.includes(member.roomId)
|
||||
) {
|
||||
|
||||
try {
|
||||
const room = await this.client.joinRoom(member.roomId)
|
||||
logger.log("info", "AUTO JOINED ROOM" + room.roomId)
|
||||
logger.log("info", "AUTO JOINED ROOM => " + room.roomId)
|
||||
this.sendTextMessage(
|
||||
this.config.FACILITATOR_ROOM_ID,
|
||||
`A support seeker requested a chat (Room ID: ${room.roomId})`
|
||||
);
|
||||
this.inviteFacilitators(room.roomId)
|
||||
} catch(err) {
|
||||
logger.log("error", "ERROR JOINING ROOM =>" + err)
|
||||
logger.log("error", "ERROR JOINING ROOM => " + err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,11 +333,9 @@ class OcrccBot {
|
||||
if (
|
||||
member.membership === "join" &&
|
||||
member.userId !== this.config.BOT_USERID &&
|
||||
this.awaitingFacilitator[member.roomId]
|
||||
this.localStorage.getItem(`${member.roomId}-waiting`)
|
||||
) {
|
||||
this.activeChatrooms[member.roomId] = {
|
||||
facilitator: member.userId
|
||||
};
|
||||
this.localStorage.setItem(`${member.roomId}-facilitator`, member.userId)
|
||||
const event = {
|
||||
getType: () => {
|
||||
return "m.room.power_levels";
|
||||
@ -380,7 +372,7 @@ class OcrccBot {
|
||||
});
|
||||
const filename = `${chatDate} - ${chatTime} - ${member.roomId}.txt`;
|
||||
const filepath = path.resolve(path.join("transcripts", filename));
|
||||
this.activeChatrooms[member.roomId].transcriptFile = filepath;
|
||||
this.localStorage.setItem(`${member.roomId}-transcript`, filepath)
|
||||
}
|
||||
}
|
||||
|
||||
@ -388,8 +380,8 @@ class OcrccBot {
|
||||
member.membership === "leave" &&
|
||||
member.userId !== this.config.BOT_USERID
|
||||
) {
|
||||
|
||||
if (this.activeChatrooms[member.roomId] && member.userId === this.activeChatrooms[member.roomId].facilitator) {
|
||||
const facilitatorId = this.localStorage.getItem(`${member.roomId}-facilitator`)
|
||||
if (member.userId === facilitatorId) {
|
||||
this.sendTextMessage(
|
||||
member.roomId,
|
||||
`${member.name} has left the chat.`
|
||||
@ -404,13 +396,15 @@ class OcrccBot {
|
||||
|
||||
if (memberCount === 1) { // just the bot
|
||||
logger.log("info", `LEAVING EMPTY ROOM ==> ${member.roomId}`);
|
||||
this.client.leave(event.roomId)
|
||||
this.client.leave(member.roomId)
|
||||
this.localStorage.removeItem(`${member.roomId}-facilitator`)
|
||||
this.localStorage.removeItem(`${member.roomId}-transcript`)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setMessageListeners() {
|
||||
async setMessageListeners() {
|
||||
// encrypted messages
|
||||
this.client.on("Event.decrypted", (event, err) => {
|
||||
if (err) {
|
||||
@ -442,6 +436,7 @@ class OcrccBot {
|
||||
|
||||
async start() {
|
||||
const localStorage = this.createLocalStorage();
|
||||
this.localStorage = localStorage
|
||||
|
||||
try {
|
||||
const auth = {
|
||||
@ -465,7 +460,7 @@ class OcrccBot {
|
||||
await this.trackJoinedRooms()
|
||||
await this.client.initCrypto()
|
||||
await this.setMembershipListeners();
|
||||
this.setMessageListeners();
|
||||
await this.setMessageListeners();
|
||||
this.client.startClient({ initialSyncLimit: 0 })
|
||||
} catch(err) {
|
||||
this.handleBotCrash(undefined, err);
|
||||
|
@ -0,0 +1,5 @@
|
||||
@help-bot:rhok.space [1:00:21 PM]: Facilitator Demo Account has joined the chat.
|
||||
@help-bot:rhok.space [1:00:22 PM]: Facilitator Demo Account has joined the chat.
|
||||
@ocrcc-facilitator-demo:rhok.space [1:00:28 PM]: hi
|
||||
@help-bot:rhok.space [1:00:42 PM]: Facilitator Demo Account has left the chat.
|
||||
@help-bot:rhok.space [1:00:42 PM]: Facilitator Demo Account has left the chat.
|
@ -0,0 +1,5 @@
|
||||
@help-bot:rhok.space [1:02:18 PM]: Facilitator Demo Account has joined the chat.
|
||||
@help-bot:rhok.space [1:02:19 PM]: Facilitator Demo Account has joined the chat.
|
||||
@ocrcc-facilitator-demo:rhok.space [1:02:23 PM]: ASDF
|
||||
@help-bot:rhok.space [1:03:01 PM]: Facilitator Demo Account has left the chat.
|
||||
@help-bot:rhok.space [1:03:02 PM]: Facilitator Demo Account has left the chat.
|
@ -0,0 +1,6 @@
|
||||
@help-bot:rhok.space [4:22:04 PM]: Facilitator Demo Account has joined the chat.
|
||||
@help-bot:rhok.space [4:22:04 PM]: Facilitator Demo Account has joined the chat.
|
||||
@ocrcc-facilitator-demo:rhok.space [4:22:15 PM]: hiiii
|
||||
@39102030-6be0-43f8-9f77-2e4c961d719a:rhok.space [4:22:18 PM]: hihi
|
||||
@ocrcc-facilitator-demo:rhok.space [4:22:25 PM]: oih
|
||||
@help-bot:rhok.space [4:22:40 PM]: Facilitator Demo Account has left the chat.
|
@ -0,0 +1,12 @@
|
||||
@help-bot:rhok.space [4:23:09 PM]: Facilitator Demo Account has joined the chat.
|
||||
@ocrcc-facilitator-demo:rhok.space [4:23:14 PM]: so
|
||||
@9a3d4822-8ba4-4931-9736-db9d0ff2fdcb:rhok.space [4:23:20 PM]: ☺️
|
||||
@9a3d4822-8ba4-4931-9736-db9d0ff2fdcb:rhok.space [4:23:27 PM]: 😄
|
||||
@help-bot:rhok.space [4:23:35 PM]: Apr 22, 2020 - 4:23:09 PM - !IsZMQlwOgpsrfZdBQo:rhok.space.txt
|
||||
@help-bot:rhok.space [4:23:35 PM]: Apr 22, 2020 - 4:23:09 PM - !IsZMQlwOgpsrfZdBQo:rhok.space.txt
|
||||
@help-bot:rhok.space [4:24:17 PM]: Apr 22, 2020 - 4:23:09 PM - !IsZMQlwOgpsrfZdBQo:rhok.space.txt
|
||||
@help-bot:rhok.space [4:24:17 PM]: Apr 22, 2020 - 4:23:09 PM - !IsZMQlwOgpsrfZdBQo:rhok.space.txt
|
||||
@help-bot:rhok.space [4:24:17 PM]: Apr 22, 2020 - 4:23:09 PM - !IsZMQlwOgpsrfZdBQo:rhok.space.txt
|
||||
@9a3d4822-8ba4-4931-9736-db9d0ff2fdcb:rhok.space [4:24:27 PM]: hey hey
|
||||
@ocrcc-facilitator-demo:rhok.space [4:24:32 PM]: cool beans
|
||||
@help-bot:rhok.space [4:24:45 PM]: Facilitator Demo Account has left the chat.
|
@ -0,0 +1,5 @@
|
||||
@help-bot:rhok.space [4:26:25 PM]: Facilitator Demo Account has joined the chat.
|
||||
@help-bot:rhok.space [4:26:25 PM]: Facilitator Demo Account has joined the chat.
|
||||
@df279781-646c-46e9-ad6a-565a4e31d0bb:rhok.space [4:26:36 PM]: so
|
||||
@ocrcc-facilitator-demo:rhok.space [4:26:41 PM]: asdf
|
||||
@help-bot:rhok.space [4:33:26 PM]: Facilitator Demo Account has left the chat.
|
@ -0,0 +1,3 @@
|
||||
@help-bot:rhok.space [4:33:45 PM]: Facilitator Demo Account has joined the chat.
|
||||
@help-bot:rhok.space [4:33:45 PM]: Facilitator Demo Account has joined the chat.
|
||||
@help-bot:rhok.space [4:34:48 PM]: Facilitator Demo Account has left the chat.
|
@ -0,0 +1,5 @@
|
||||
@help-bot:rhok.space [4:50:37 PM]: Facilitator Demo Account has joined the chat.
|
||||
@help-bot:rhok.space [4:50:37 PM]: Facilitator Demo Account has joined the chat.
|
||||
@ocrcc-facilitator-demo:rhok.space [4:50:45 PM]: hihi
|
||||
@edd84c88-b2ab-4910-9eeb-595881f1c145:rhok.space [4:50:56 PM]: asdfasdf
|
||||
@help-bot:rhok.space [4:51:04 PM]: Facilitator Demo Account has left the chat.
|
@ -0,0 +1,5 @@
|
||||
@help-bot:rhok.space [4:58:09 PM]: Facilitator Demo Account has joined the chat.
|
||||
@help-bot:rhok.space [4:58:10 PM]: Facilitator Demo Account has joined the chat.
|
||||
@ocrcc-facilitator-demo:rhok.space [4:58:28 PM]: kj
|
||||
@e5221f47-38ce-47ae-b1c7-2921bd035362:rhok.space [4:58:34 PM]: kk
|
||||
@help-bot:rhok.space [4:58:47 PM]: Facilitator Demo Account has left the chat.
|
@ -0,0 +1,2 @@
|
||||
@help-bot:rhok.space [5:28:23 PM]: Facilitator Demo Account has joined the chat.
|
||||
@help-bot:rhok.space [5:28:24 PM]: Facilitator Demo Account has joined the chat.
|
Loading…
Reference in New Issue
Block a user