Beispiel #1
0
    async def _handle_account(self, parts: List[str]) -> str:
        """
        Handle account specific commands received from client
        """

        # prepare everything for the actual command handling later
        acc_id = -1
        params = []
        if parts[1] == "list":
            # special case for "list" command
            command = parts[1]
        elif parts[1] == "add":
            # special case for "add" command
            command = parts[1]
            params = parts[2:]
        elif len(parts) >= 3:
            # account specific commands
            try:
                acc_id = int(parts[1])
            except ValueError:
                return Message.error("invalid account ID")
            command = parts[2]
            params = parts[3:]
            # valid account?
            if acc_id not in self.account_list.get().keys():
                return Message.error("invalid account")
        else:
            # invalid command, ignore
            return Message.error("invalid command")

        return await self._handle_account_command(command, acc_id, params)
Beispiel #2
0
    def get_buddies(self, online: bool) -> None:
        """
        Get roster/buddy list
        """

        # if we are offline, there are no buddies
        if self.client.status == "offline":
            return

        # if only online wanted, skip because no matrix room is "online"
        if online:
            return

        # get buddies/rooms
        rooms = self.client.get_rooms()
        for room in rooms.values():
            name = escape_name(room.display_name)

            # use special status for group chats
            status = "GROUP_CHAT"

            # send buddy message
            msg = Message.buddy(self.account, room.room_id, name, status)
            self.account.receive_msg(msg)

        # handle pending room invites as temporary buddies
        invites = self.client.get_invites()
        for invite in invites.values():
            room_id, room_name, _sender, _sender_name, _tstamp = invite
            status = "GROUP_CHAT_INVITE"

            # send buddy message
            msg = Message.buddy(self.account, room_id, room_name, status)
            self.account.receive_msg(msg)
Beispiel #3
0
    async def handle_account_list(self) -> str:
        """
        List all accounts
        """

        replies = []
        accounts = self.account_list.get()
        for acc in accounts.values():
            reply = Message.account(acc)
            replies.append(reply)

        # inform caller that all accounts have been received
        replies.append(Message.info("listed accounts."))

        # add account add help if there are no accounts
        if not accounts:
            replies.append(await self.callbacks.call(Callback.HELP_ACCOUNT_ADD,
                                                     None, ()))

        # log event
        log_msg = f"account list: {replies}"
        logging.info(log_msg)

        # return a single string
        return "".join(replies)
Beispiel #4
0
    async def _help_welcome(self, _account: Optional["Account"],
                            _cmd: Callback, _params: Tuple) -> str:
        """
        Handle welcome help message event
        """

        welcome = Message.info(f"Welcome to nuqql-matrixd v{VERSION}!")
        welcome += Message.info("Enter \"help\" for a list of available "
                                "commands and their help texts")
        if self.based.config.get_push_accounts():
            welcome += Message.info("Listing your accounts:")
        return welcome
Beispiel #5
0
    async def _help_account_add(_account: Optional["Account"], _cmd: Callback,
                                _params: Tuple) -> str:
        """
        Handle account add help event
        """

        add_help = Message.info("You do not have any accounts configured.")
        add_help += Message.info("You can add a new matrix account with the "
                                 "following command: "
                                 "account add matrix <username>@<homeserver> "
                                 "<password>")
        add_help += Message.info("Example: account add matrix "
                                 "[email protected] MyPassword")
        return add_help
Beispiel #6
0
    def _get_status(self) -> None:
        """
        Get the current status of the account
        """

        self.account.receive_msg(
            Message.status(self.account, self.client.status))
Beispiel #7
0
    async def _handle_account_collect(self, acc_id: int,
                                      params: List[str]) -> str:
        """
        Collect messages for a specific account.

        Expected format:
            account <ID> collect [time]

        params does not include "account <ID> collect"
        """

        # collect all messages since <time>?
        time = 0  # TODO: change it to time of last collect?
        if len(params) >= 1:
            time = int(params[0])

        # log event
        log_msg = f"account {acc_id} collect {time}"
        logging.info(log_msg)

        # collect messages
        accounts = self.account_list.get()
        acc = accounts[acc_id]
        history = acc.get_history()
        # TODO: this expects a list. change to string? document list req?
        history += await self.callbacks.call(Callback.COLLECT_MESSAGES, acc,
                                             ())

        # append info message to notify caller that everything was collected
        history += [Message.info(f"collected messages for account {acc_id}.")]

        # return history as single string
        return "".join(history)
Beispiel #8
0
    async def _handle_account_command(self, command: str, acc_id: int,
                                      params: List[str]) -> str:
        if command == "list":
            return await self.handle_account_list()

        if command == "add":
            # currently this supports "account <ID> add" and "account add <ID>"
            # if the account ID is valid
            return await self._handle_account_add(params)

        if command == "delete":
            return await self._handle_account_delete(acc_id)

        # handle other commands with same parameters
        command_map = {
            "buddies": self._handle_account_buddies,
            "collect": self._handle_account_collect,
            "send": self._handle_account_send,
            "status": self._handle_account_status,
            "chat": self._handle_account_chat,
        }
        if command in command_map:
            return await command_map[command](acc_id, params)

        return Message.error("unknown command")
Beispiel #9
0
    def _chat_join(self, chat: str) -> None:
        """
        Join chat on account
        """

        error = self.client.join_room(chat)
        if error != "":
            self.account.receive_msg(Message.error(error))
Beispiel #10
0
    def _chat_create(self, name: str) -> None:
        """
        Create a group chat room with name <name>
        """

        error = self.client.create_room(name)
        if error != "":
            self.account.receive_msg(Message.error(error))
Beispiel #11
0
    def _chat_part(self, chat: str) -> None:
        """
        Leave chat on account
        """

        error = self.client.part_room(chat)
        if error != "":
            self.account.receive_msg(Message.error(error))
Beispiel #12
0
    def _chat_invite(self, chat: str, user_id: str) -> None:
        """
        Invite user to chat
        """

        error = self.client.invite_room(chat, user_id)
        if error != "":
            self.account.receive_msg(Message.error(error))
Beispiel #13
0
async def get_status(acc: Optional["Account"], _cmd: Callback,
                     _params: Tuple) -> str:
    """
    Get the status of the account
    """

    assert acc
    acc.receive_msg(Message.status(acc, acc.status))
    return ""
Beispiel #14
0
    async def add(self,
                  acc_type: str,
                  acc_user: str,
                  acc_pass: str,
                  acc_id: int = None) -> str:
        """
        Add a new account
        """

        # make sure the account does not exist
        for acc in self.accounts.values():
            if acc.type == acc_type and acc.user == acc_user:
                return Message.info("account already exists.")

        # get a free account id if none is given
        if acc_id is None:
            acc_id = self._get_free_account_id()

        # create account and add it to list
        new_acc = Account(config=self.config,
                          callbacks=self.callbacks,
                          queue=self.queue,
                          aid=acc_id)
        new_acc.type = acc_type
        new_acc.user = acc_user
        new_acc.password = acc_pass
        self.accounts[new_acc.aid] = new_acc

        # store updated accounts in file
        self.store()

        # log event
        log_msg = (f"account new: id {new_acc.aid} type {new_acc.type} "
                   f"user {new_acc.user}")
        logging.info(log_msg)

        # notify callback (if present) about new account
        await self.callbacks.call(Callback.ADD_ACCOUNT, new_acc, ())

        # return result
        result = Message.info(f"added account {new_acc.aid}.")
        if self.config.get_push_accounts():
            result += Message.account(new_acc)
        return result
Beispiel #15
0
    def _chat_list(self) -> None:
        """
        List active chats of account
        """

        rooms = self.client.get_rooms()
        for room in rooms.values():
            self.account.receive_msg(
                Message.chat_list(self.account, room.room_id,
                                  escape_name(room.display_name), self.user))
Beispiel #16
0
    def receive_msg(self, msg: str) -> None:
        """
        Receive a message from other users or the backend
        """

        self.queue.put_nowait(msg)

        if Message.is_message(msg) and self.config.get_history():
            # TODO: add timestamp?
            self._history.append(msg)
Beispiel #17
0
async def set_status(acc: Optional["Account"], _cmd: Callback,
                     params: Tuple) -> str:
    """
    Set the status of the account
    """

    assert acc
    status = params[0]
    acc.status = status
    acc.receive_msg(Message.status(acc, status))
    return ""
Beispiel #18
0
    async def _handle_version(self) -> Tuple[str, str]:
        """
        Handle the version command received from client
        """

        msg = await self.callbacks.call(Callback.VERSION, None, ())
        if not msg:
            name = self.config.get_name()
            version = self.config.get_version()
            msg = f"version: {name} v{version}"
        return ("msg", Message.info(msg))
Beispiel #19
0
    def _chat_users(self, chat: str) -> None:
        """
        Get list of users in chat on account
        """

        user_list = self.client.list_room_users(chat)
        for user in user_list:
            user_id, user_name, user_status = user
            self.account.receive_msg(
                Message.chat_user(self.account, chat, user_id, user_name,
                                  user_status))
Beispiel #20
0
    async def _handle_account_delete(self, acc_id: int) -> str:
        """
        Delete an existing account

        Expected format:
            account <ID> delete
        """

        # delete account
        result = await self.account_list.delete(acc_id)

        # inform caller about result
        return Message.info(result)
Beispiel #21
0
    def _membership_event(self, *params):
        """
        Handle membership event
        """

        # parse params
        event_type, tstamp, sender_id, sender_name, room_id, room_name,\
            invited_user = params

        # check membership type
        if event_type == "invite":
            user_msg = Message.chat_user(self.account, room_id, invited_user,
                                         invited_user, event_type)
            msg = (f"*** {sender_name} invited {invited_user} "
                   f"to {room_name}. ***")

        if event_type == "join":
            user_msg = Message.chat_user(self.account, room_id, sender_id,
                                         invited_user, event_type)
            msg = f"*** {invited_user} joined {room_name}. ***"

        if event_type == "leave":
            user_msg = Message.chat_user(self.account, room_id, sender_id,
                                         sender_name, event_type)
            msg = f"*** {sender_name} left {room_name}. ***"

        # generic event, return as message
        # TODO: change parsing in nuqql and use char + / + sender here?
        formatted_msg = Message.CHAT_MSG.format(self.account.aid, room_id,
                                                tstamp, sender_id, msg)

        # add event to event list
        if self.settings.membership_user_msg:
            self.account.receive_msg(user_msg)
        if self.settings.membership_message_msg:
            self.account.receive_msg(formatted_msg)
Beispiel #22
0
async def send_message(acc: Optional["Account"], _cmd: Callback,
                       params: Tuple) -> str:
    """
    Send a message to another user. For testing, this simply modifies the
    message and returns it to the sender.
    """

    assert acc
    dest, msg = params

    # add destination as buddy in the testing buddy list
    _add_buddy(dest)

    acc.receive_msg(
        Message.message(acc, str(int(time.time())), dest, acc.user,
                        msg.upper()))
    return ""
Beispiel #23
0
    def _message(self, tstamp, sender, room_id, msg) -> None:
        """
        Message handler
        """

        # if filter_own is set, skip own messages
        if self.account.config.get_filter_own() and sender == self.user:
            return

        # rewrite sender of own messages
        if sender == self.user:
            sender = "<self>"

        # save timestamp and message in messages list and history
        formatted_msg = Message.chat_msg(self.account, tstamp, sender, room_id,
                                         msg)
        self.account.receive_msg(formatted_msg)
Beispiel #24
0
    async def _handle_account_buddies(self, acc_id: int,
                                      params: List[str]) -> str:
        """
        Get buddies for a specific account. If params contains "online", filter
        online buddies.

        Expected format:
            account <ID> buddies [online]

        params does not include "account <ID> buddies"

        Returned messages should look like:
            buddy: <acc_id> status: <Offline/Available> name: <name> alias:
                <alias>
        """

        # get account
        accounts = self.account_list.get()
        acc = accounts[acc_id]

        # filter online buddies?
        online = False
        if len(params) >= 1 and params[0].lower() == "online":
            online = True

        # update buddy list
        result = await self.callbacks.call(Callback.GET_BUDDIES, acc,
                                           (online, ))

        # add info message that all buddies have been received
        info = Message.info(f"got buddies for account {acc_id}.")

        # log event
        log_msg = f"account {acc_id} buddies: {result}"
        logging.info(log_msg)

        # return replies as single string
        return result + info
Beispiel #25
0
async def _get_buddies(acc: Optional["Account"], _cmd: Callback,
                       params: Tuple) -> str:
    """
    Get buddy list. For testing, this simply returns a single buddy list for
    all accounts.
    """

    # filter online users?
    online = False
    if params:
        online = params[0]

    # no test buddies are online, return empty list if only online wanted
    if online:
        return ""

    # construct buddy messages
    result = ""
    assert acc
    for buddy in TEST_BUDDIES:
        result += Message.buddy(acc, buddy, "", "")

    return result
Beispiel #26
0
    async def _handle_account_status(self, acc_id: int,
                                     params: List[str]) -> str:
        """
        Get or set current status of account

        Expected format:
            account <ID> status get
            account <ID> status set <STATUS>

        params does not include "account <ID> status"

        Returned messages for "status get" should look like:
            status: account <ID> status: <STATUS>
        """

        if not params:
            return ""

        # get account
        accounts = self.account_list.get()
        acc = accounts[acc_id]

        # get current status
        if params[0] == "get":
            status = await self.callbacks.call(Callback.GET_STATUS, acc, ())
            if status:
                return Message.status(acc, status)

        # set current status
        if params[0] == "set":
            if len(params) < 2:
                return ""

            status = params[1]
            return await self.callbacks.call(Callback.SET_STATUS, acc,
                                             (status, ))
        return ""