grigori-gru/matrix-cli

View on GitHub
src/lib/actions.js

Summary

Maintainability
D
1 day
Test Coverage
const { formatName } = require('./utils');
const chalk = require('chalk');
const myLogger = require('./logger');
// eslint-disable-next-line
const MatrixService = require('./matrix-service');
// eslint-disable-next-line
const Ask = require('./questions');

const DEFAULT_LIMIT = 6;

module.exports = class {
    /**
     * Service to work with input from console and matrix service
     * @param {MatrixService} matrixService service to work with matrix
     * @param {Ask} ask service to input data
     * @param {Console} logger logger myLogger by default
     */
    constructor(matrixService, ask, logger = myLogger) {
        this.matrixService = matrixService;
        this.ask = ask;
        this.logger = logger;
    }

    /**
     *
     * @param {object} roomData rooms data
     */
    _printRoomDate({ roomName, date }) {
        this.logger.log('\t-----------------------------------------------------');
        this.logger.log(chalk.blue('room name              '), chalk.yellow(roomName));
        if (date) {
            this.logger.log(chalk.blue('date of last activity  '), chalk.yellow(date));
        }
    }

    /**
     * Leave room recursive method
     * @param {Room[]} rooms
     * @param {{deleteAlias: boolean}} [options]
     * @return {Promise<{errors: Error[], leavedRooms: Room[], errLeavedRooms: Room[]} | undefined>} errors and leaved rooms
     */
    async _runLeaving(rooms, options) {
        const res = await this.matrixService.leaveRooms(rooms, options);
        const { errors, leavedRooms, errLeavedRooms } = res;
        leavedRooms.length && this.logger.log(chalk.green(`\nYou have leaved ${leavedRooms.length} rooms!!!\n`));

        errLeavedRooms.length &&
            this.logger.log(chalk.green(`\nYou couldn't leave ${errLeavedRooms.length} rooms!!!\n`));

        errors.length && (await this.ask.isShowErrors()) && this.logger.error(errors);

        const isSave = await this.ask.isSaveLeavedToFile();
        if (isSave) {
            const pathToFile = await this.matrixService.saveToJson(leavedRooms, 'leaved');
            this.logger.log(chalk.blue('\nPath to file: '), chalk.yellow(pathToFile));
        }

        return errLeavedRooms.length && (await this.ask.tryAgainForErrors()) ? this._runLeaving(errLeavedRooms) : res;
    }

    /**
     * Get rooms by user params and leave them
     * @return {Promise<{errors: Error[], leavedRooms: Room[], errLeavedRooms: Room[]} | undefined>} errors and leaved rooms
     */
    async leaveByDate() {
        const limit = await this.ask.limitMonths(DEFAULT_LIMIT);

        const rooms = await this.matrixService.getRooms(limit);

        if (!rooms.length) {
            this.logger.log(chalk.yellow("You don't have any room to leave"));
            return;
        }

        const ignoreMsg = this.matrixService.ignoreUsers.length
            ? `of users (${this.matrixService.ignoreUsers.join(', ')}) `
            : '';
        const infoRoomMsg = `\nWe found ${rooms.length} rooms where last activity ${ignoreMsg}was ${limit} months ago\n`;
        this.logger.log(chalk.green(infoRoomMsg));

        const selectedRooms = await this.ask.selectRooms(rooms);

        if (selectedRooms.length) {
            (await this.ask.isShowRooms()) &&
                selectedRooms.map(room =>
                    this._printRoomDate({ roomName: room.roomName, date: room.lastMessageDate.date }),
                );

            if (await this.ask.isLeave()) {
                return this._runLeaving(selectedRooms);
            }
        }
    }

    /**
     * Get rooms by user params and leave them
     * @return {Promise<{errors: Error[], leavedRooms: Room[], errLeavedRooms: Room[]} | undefined>} errors and leaved rooms
     */
    async leaveEmpty() {
        const rooms = await this.matrixService.noMessagesEmptyRooms();
        if (!rooms.length) {
            this.logger.log(chalk.green('\nWe dont found empty rooms with no messages\n'));
            return;
        }

        this.logger.log(chalk.green(`\nWe found ${rooms.length} empty rooms\n`));
        const selectedRooms = await this.ask.selectRooms(rooms);

        if (selectedRooms.length && (await this.ask.isLeave())) {
            return this._runLeaving(selectedRooms, { deleteAlias: true });
        }
    }

    /**
     * Leave all rooms where some user exists
     * @return {Promise<{errors: array, leavedRooms: Rooms[], errLeavedRooms: Rooms[]}>} result of invite
     */
    async leaveByMember() {
        const matrixUser = await this._selectUser();

        if (!matrixUser) {
            this.logger.log(chalk.yellow('\nNo user selected!!!\n'));
            return;
        }

        const userId = formatName(matrixUser);
        const { allRooms } = await this.matrixService.getAllRoomsInfo();

        const memberRooms = allRooms.filter(room => {
            return room.members.includes(userId);
        });
        if (!memberRooms.length) {
            this.logger.log(chalk.yellow(`\nNo room with user ${userId} is found\n`));
            return;
        }

        this.logger.log(chalk.green(`\nWe found ${memberRooms.length} with joined member ${userId}\n`));
        const selectedRooms = await this.ask.selectRooms(memberRooms);

        if (selectedRooms.length && (await this.ask.isLeave())) {
            return this._runLeaving(selectedRooms);
        }
    }

    /**
     * Join to room from list
     * @return {Promise<{errors: Error[], joinedRooms: []}>} result of invite
     */
    async join() {
        const { allRooms } = await this.matrixService.getAllRoomsInfo();
        const notJoinedRooms = allRooms.filter(room => !room.members.includes(this.matrixService.userName));
        if (notJoinedRooms.length === 0) {
            this.logger.log(chalk.yellow(`\nAll rooms, where you are invited, you have alredy joned!\n`));
            return;
        }
        this.logger.log(chalk.green(`\nWe found ${notJoinedRooms.length} rooms you are invited but not joined\n`));

        const iter = async rooms => {
            const res = await this.matrixService.join(rooms);
            const { joinedRooms, errors, errJoinedRooms } = res;
            const isSave = await this.ask.isSaveLeavedToFile();

            joinedRooms.length && this.logger.log(chalk.green(`\nYou have joined to ${joinedRooms.length} rooms!!!\n`));

            errJoinedRooms.length &&
                this.logger.log(chalk.green(`\nYou couldn't join to ${errJoinedRooms.length} rooms!!!\n`));

            errors.length && (await this.ask.isShowErrors()) && this.logger.error(errors);

            if (isSave) {
                const pathToFile = await this.matrixService.saveToJson(joinedRooms, 'joined');
                this.logger.log(chalk.blue('\nPath to file: '), chalk.yellow(pathToFile));
            }

            return errJoinedRooms.length && (await this.ask.tryAgainForErrors()) ? iter(errJoinedRooms) : res;
        };

        if (await this.ask.isJoin()) {
            return iter(notJoinedRooms);
        }
    }

    /**
     * Select user from existing or print name
     * @param {string[]} [users] users to select
     * @return {string} user id
     */
    async _selectUser(users) {
        const selectWays = {
            existing: async () => {
                const knownUsers = users || await this.matrixService.getknownUsers();
                return this.ask.selectUser(knownUsers);
            },
            print: async () => {
                const user = await this.ask.inputOne();

                return user && this.matrixService.getUserId(user);
            },
        };
        const userStrategy = await this.ask.selectUserStrategy();

        return selectWays[userStrategy]();
    }

    /**
    * Select user from existing or print name
    * @return {Room} user id
    */
    async _selectRoom() {
        const selectWays = {
            existing: async () => {
                const { allRooms } = await this.matrixService.getAllRoomsInfo();
                return this.ask.selectRoomByInput(allRooms);
            },
            print: async () => {
                const room = await this.ask.inputOne();

                return room;
            },
        };
        const userStrategy = await this.ask.selectUserStrategy();

        return selectWays[userStrategy]();
    }

    /**
     * Get all available rooms and invite selected user
     * @return {Promise<{userId: string, errors: array, invitedRooms: []}>} result of invite
     */
    async setPower() {
        const userId = await this._selectUser();

        if (!userId) {
            this.logger.log(chalk.yellow('\nNo user is selected\n'));
            return;
        }

        const roomsInfo = await this.matrixService.getAllRoomsInfo();
        const strategy = await this.ask.selectStrategy();
        const userMemberRooms = roomsInfo[strategy].filter(room => room.members.includes(formatName(userId)));
        if (userMemberRooms.length === 0) {
            this.logger.log(chalk.yellow(`\nIn group ${strategy} no rooms with ${userId} found\n`));
            return;
        }
        const poweredRooms = await this.ask.selectRooms(userMemberRooms);
        if (poweredRooms.length === 0) {
            this.logger.log(chalk.yellow(`\nIn group ${strategy} no rooms found\n`));
            return;
        }

        const iter = async rooms => {
            const res = await this.matrixService.setPower(rooms, userId);
            const { poweredRooms, errPoweredRooms, errors } = res;
            const isSave = await this.ask.isSaveLeavedToFile();

            poweredRooms.length &&
                this.logger.log(chalk.green(`\nYou have set power to ${poweredRooms.length} rooms!!!\n`));

            errPoweredRooms.length &&
                this.logger.log(chalk.green(`\nYou couldn't set power to ${errPoweredRooms.length} rooms!!!\n`));

            errors.length && (await this.ask.isShowErrors()) && this.logger.error(errors);

            if (isSave) {
                const pathToFile = await this.matrixService.saveToJson(poweredRooms, 'powered');
                this.logger.log(chalk.blue('\nPath to file: '), chalk.yellow(pathToFile));
            }

            return errPoweredRooms.length && (await this.ask.tryAgainForErrors()) ? iter(errPoweredRooms) : res;
        };

        if (await this.ask.isPowered()) {
            return iter(poweredRooms);
        }
    }

    /**
     * kick user
     */
    async kickUser() {
        const room = await this._selectRoom();

        if (!room) {
            this.logger.log(chalk.yellow('\nNo user is selected\n'));
            return;
        }

        this.logger.info('Selected room with id ' + room.roomId);

        const userIds = room.members.map(this.matrixService.getUserId.bind(this.matrixService));

        const userId = await this._selectUser(userIds);

        if (!userId) {
            this.logger.log(chalk.yellow('\nNo user is selected\n'));
            return;
        }

        const res = await this.matrixService.kickUser(userId, room.roomId);
        if (res) {
            this.logger.info(chalk.green(`\nUser ${userId} is kicked from room with id ${room.roomId}!!!\n`));

            return true;
        }

        this.logger.warn('Something wrong. User ' + userId + ' is not kicked from room with id ' + room.roomId);

        return true;
    }

    /**
     * Get all available rooms and invite selected user
     * @return {Promise<{userId: string, errors: array, invitedRooms: []}>} result of invite
     */
    async invite() {
        const rooms = await this.matrixService.getAllRoomsInfo();
        const strategy = await this.ask.selectStrategy();
        const inviteRooms = await this.ask.selectRooms(rooms[strategy]);
        if (inviteRooms.length === 0) {
            this.logger.log(chalk.yellow(`\nIn group ${strategy} no rooms found\n`));
            return;
        }

        const userId = await this._selectUser();

        if (!userId) {
            this.logger.log(chalk.yellow('\nNo user is selected\n'));
            return;
        }

        if (await this.ask.isInvite()) {
            const { errors, invitedRooms, errInvitedRooms } = await this.matrixService.inviteUserToRooms(
                inviteRooms,
                userId,
            );
            errors.length && (await this.ask.isShowErrors()) && this.logger.error(errors);
            const isSave = await this.ask.isSaveLeavedToFile();
            if (isSave) {
                const pathToFile = await this.matrixService.saveToJson(invitedRooms, 'invited');
                this.logger.log(chalk.blue('\nPath to file: '), chalk.yellow(pathToFile));
            }

            return { errors, invitedRooms, invitedUser: userId, errInvitedRooms };
        }
    }

    /**
     * @return {Promise<{allRooms: Room[], singleRoomsManyMessages: Room[], singleRoomsNoMessages: Room[], manyMembersNoMessages: Room[], manyMembersManyMessages: Room[]}>} matrix rooms
     */
    async getRoomsInfo() {
        const info = await this.matrixService.getAllRoomsInfo();

        Object.entries(info).map(([key, rooms]) => {
            this.logger.log(chalk.blue('\n' + key + ' is ' + rooms.length));
        });
        const isSave = await this.ask.isSaveLeavedToFile();
        if (isSave) {
            const pathToFile = await this.matrixService.saveToJson(info, 'info');
            this.logger.log(chalk.blue('\nPath to file: '), chalk.yellow(pathToFile));
        }

        return info;
    }

    /**
     *
     * @param {string?} room room name
     * @param {string?} optionalMessage message from command line
     */
    async send(room, optionalMessage) {
        const { allRooms } = await this.matrixService.getAllRoomsInfo();

        const rooms = room ? await this.matrixService.getRoomByName(room) : await this.ask.selectRooms(allRooms);

        if (rooms.length === 0) {
            this.logger.warn('No room selected!');
            return;
        }

        const message = optionalMessage || (await this.ask.inputMessage());

        if (message) {
            const errors = await this.matrixService.sendMessage(rooms, message);
            if (errors) {
                optionalMessage
                    ? this.logger.error(errors)
                    : (await this.ask.isShowErrors()) && this.logger.error(errors);
            }
        }
    }

    /**
     * Get room id by alias
     * @return {string?} roomId
     */
    async getIdByAlias() {
        const alias = await this.ask.inputRoomAlias();
        if (!alias) {
            return;
        }

        const roomId = await this.matrixService.getRoomByAlias(alias);
        const msg = roomId ? roomId : `not found`;
        this.logger.log(chalk.blue(`\nMatrix id for room with alias ${alias} is ${msg}\n`));

        return roomId;
    }

    /**
     * Delete analias form matrix
     */
    async deleteAlias() {
        const aliasPart = await this.ask.inputRoomAlias();
        if (!aliasPart) {
            return;
        }

        const roomId = await this.matrixService.getRoomByAlias(aliasPart);

        if (roomId) {
            this.logger.log(chalk.blue(`\nMatrix id for room with alias ${aliasPart} is ${roomId}\n`));

            if (await this.ask.isDeleteAlias()) {
                await this.matrixService.deleteAlias(aliasPart);

                this.logger.log(chalk.green(`\nAlias ${aliasPart} for room ${roomId} is successfully deleted!!!\n`));

                return roomId;
            }

            return;
        }

        this.logger.log(chalk.yellow(`\nMatrix id for room with alias ${aliasPart} is not found\n`));
    }
};