leave rooms when empty, use localStorage

This commit is contained in:
Sharon Kennedy
2020-04-22 18:04:48 -04:00
parent 39d97343a5
commit aef215bd7e
14 changed files with 758 additions and 440 deletions

796
dist/bot.js vendored

File diff suppressed because it is too large Load Diff

253
dist/bot.test.js vendored Normal file
View 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
View File

@@ -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
View File

@@ -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;