Beispiel #1
0
def disconnect(bot: Bot, update: Updater, user_data: dict):
    """
    Closes the open SSH connection to the bridge computer.

    After that, sends a 'Disconnected' message to the chat and returns to the
    main menu.

    Args:
        bot (:obj:`telegram.bot.Bot`): The telegram bot instance.
        update (:obj:`telegram.ext.update.Updater`): The Updater associated to
            the bot.
        user_data (:obj:`dict`): The dictionary with user variables.
    """

    bridge_ip = Session.get_from(user_data).bridge_ip

    # Save the new status of the computers
    Session.get_from(user_data).computers.save()

    # Close the connection
    Session.get_from(user_data).end_connetion()

    # Send the disconnect output
    view.disconnect_output(bridge_ip).edit(update)

    # Show the main menu again
    menu.new_main(bot, update, user_data)
Beispiel #2
0
def job_update_computers_status(bot: Bot, job: Job):
    user_data = job.context

    if Session.get_from(user_data).connected:
        update_computers_status(user_data)
    else:
        job.schedule_removal()
Beispiel #3
0
    def __run_on_remote(self, computer: Computer, session: Session):

        if not computer.on():
            return (computer.name, computer.ip, "The computer is off", "")

        session.copy_to_remote(computer.ip, self.bridge_path, self.remote_path)

        # Replace the computer arguments (ip, mac...)
        self.fill_computer_arguments(computer)
        command = f"{self.remote_path} {' '.join(self.arguments.values())}"

        return (
            computer.name,
            computer.ip,
            (*session.run_on_remote(computer.ip, command, self.root)),
        )
Beispiel #4
0
def get_username(bot: Bot, update: Updater, user_data: dict) -> int:
    """Get the username from the last messge. Ask for the password and wait"""

    Session.get_from(user_data).username = update.message.text

    view.ask_password().reply(update)

    return PASSWORD
Beispiel #5
0
def get_password(bot: Bot, update: Updater, user_data: dict,
                 job_queue) -> ConversationHandler:
    """Get the password from the last message. End conversation"""

    Session.get_from(user_data).password = update.message.text

    general.connect(bot, update, user_data, job_queue)

    return ConversationHandler.END
Beispiel #6
0
def confirm_connect_ip(bot: Bot, update: Updater, user_data: dict):

    # Get the clicked ip
    Session.get_from(user_data).bridge_ip = update.callback_query.data

    # View
    text = f"Connect to {Session.get_from(user_data).bridge_ip}"
    view.yes_no(
        text, yes_callback_data=State.GET_CREDENTIALS, no_callback_data=State.MAIN
    ).edit(update)
Beispiel #7
0
def select_department(bot: Bot, update: Updater, user_data: dict):
    """
    Show the nodes on the first level of the :obj:`structure` field on the
    :obj:`config.json` file.

    Args:
        bot (:obj:`telegram.bot.Bot`): The telegram bot instance.
        update (:obj:`telegram.ext.update.Updater`): The Updater associated to
            the bot.
        user_data (:obj:`dict`): The dictionary with user variables.
    """

    Session.get_from(user_data).route = []
    route = Session.get_from(user_data).route

    # View
    view.structure(
        route, states.config_file.get_sections(route), return_to=State.MAIN
    ).edit(update)
Beispiel #8
0
def execute_plugin(bot: Bot, update: Updater, user_data: dict):

    session = Session.get_from(user_data)
    plugin: Plugin = user_data["plugin"]

    for name, ip, stdout, stderr in plugin.run(session):
        view.plugin_output(name, ip, plugin.name, stdout,
                           stderr).reply(update, parse_mode=None)

    menu.new_main(bot, update, user_data)

    return ConversationHandler.END
Beispiel #9
0
def initialize_bridge(bot: Bot, update: Updater, user_data: dict):

    session = Session.get_from(user_data)

    plugin = Plugin("plugins/_bridge_initialization")

    name, ip, stdout, stderr = next(plugin.run(session))
    view.plugin_output(name,
                       ip,
                       "Bridge Initialization",
                       stdout,
                       stderr,
                       hide_header=True).reply(update)
Beispiel #10
0
def update_computers_status(user_data: dict):

    session = Session.get_from(user_data)

    plugin = Plugin("plugins/_computers_status")
    _, _, stdout, _ = next(plugin.run(session))
    # The stdout has the format `{ip} is {status}`

    # TODO: Remove n^2 loop by adding dicts to the computers_json structure
    for line in stdout.splitlines():
        ip, _, status = line.split()

        for computer in session.computers:
            if ip == computer.ip:
                computer.status = status
Beispiel #11
0
    def run(self, session: Session):
        session.copy_to_bridge(self.server_path, self.bridge_path, Plugin.COPY_MODE)

        # Replace the session $arguments (username, password...)
        self.fill_session_arguments(session)

        # RUN ON BRIDGE
        if self.source == PluginVar.SOURCE_BRIDGE:
            command = f"{self.bridge_path} {' '.join(self.arguments.values())}"

            yield "Bridge", session.bridge_ip, (
                *session.run_on_bridge(command, root=self.root)
            )

        # RUN ON REMOTE
        else:
            with futures.ThreadPoolExecutor() as executor:
                result_futures = [
                    executor.submit(self.__run_on_remote, computer, session)
                    for computer in session.computers.get_included_computers()
                ]

                for future in futures.as_completed(result_futures):
                    yield future.result()
Beispiel #12
0
def ip_selection(bot: Bot, update: Updater, user_data: dict):
    """
    Show a menu with the list of the computers that have a defined 'ip' field
    in the corresponding .json file
    """

    route = Session.get_from(user_data).route

    # Get the clicked section
    next_section = update.callback_query.data
    route.append(next_section)

    # Create a path to the .json file from the route
    filepath = f"config/{'/'.join(route)}.json"
    Session.get_from(user_data).computers = Computers(filepath)

    Session.get_from(user_data).route = route

    # View
    view.ip_selection(
        route,
        Session.get_from(user_data).computers.get_computers(),
        return_to=State.CONNECT,
    ).edit(update)
Beispiel #13
0
def main(bot: Bot, update: Updater, user_data: dict):
    """
    Edit the last message to show the main menu. Depending on if the user is
    connected or disconnected the view may change.

    Args:
        bot (:obj:`telegram.bot.Bot`): The telegram bot instance.
        update (:obj:`telegram.ext.update.Updater`): The Updater associated to
            the bot.
        user_data (:obj:`dict`): The dictionary with user variables.
    """

    session = Session.get_from(user_data)

    # View
    if not session.connected:
        view.not_connected().edit(update)
    else:
        view.connected(session, Plugin.get_local_plugins()).edit(update)
Beispiel #14
0
def update_ips(bot: Bot, update: Updater, user_data: dict):
    """
    Get all the macs and its associated ips from the local network. Then,
    iterate through all the computers in :obj:`Computers`. If one of the
    computer macs match with one of the local macs, update its associated ip to
    the new value.

    Args:
        bot (:obj:`telegram.bot.Bot`): The telegram bot instance.
        update (:obj:`telegram.ext.update.Updater`): The Updater associated to
            the bot.
        user_data (:obj:`dict`): The dictionary with user variables.
    """
    session = Session.get_from(user_data)

    # Get all the local ips for every local mac
    plugin = Plugin("plugins/_local_arp_scan")

    # Change view to executing
    view.plugin_start(plugin.name).edit(update)

    _, _, stdout, _ = next(plugin.run(session))

    local_ips = {}
    for line in stdout.splitlines():
        ip, mac = line.strip().split()
        local_ips[mac] = ip

    for computer in session.computers.get_included_computers():
        if computer.mac.lower() in local_ips:
            last_ip = computer.ip
            computer.ip = local_ips[computer.mac.lower()]

            view.update_ip_output(computer, last_ip).reply(update)

    session.computers.save()

    menu.new_main(bot, update, user_data)
Beispiel #15
0
def connect(bot: Bot, update: Updater, user_data: dict, job_queue: JobQueue):
    """
    Tries to open a SSH connection from the bot server to the bridge computer.

    After that, sends a message to the chat with the result of the connection
    (successed or failed) and returns to the main menu.

    Args:
        bot (:obj:`telegram.bot.Bot`): The telegram bot instance.
        update (:obj:`telegram.ext.update.Updater`): The Updater associated to
            the bot.
        user_data (:obj:`dict`): The dictionary with user variables.
    """

    session = Session.get_from(user_data)

    # Try to connect to the client
    session.start_connection()

    # Send the status message
    view.connect_output(session).reply(update)

    if session.connected:
        # Check if the bridge computer has all the required dependencies
        initialize_bridge(bot, update, user_data)

        # Check the status of each computers (which are alive or unreachable)
        update_computers_status(user_data)

        job_queue.run_repeating(
            job_update_computers_status,
            interval=states.config_file.check_status_interval,
            context=user_data,
        )

    # Show the main menu again
    menu.new_main(bot, update, user_data)
Beispiel #16
0
def start_plugin_from_download(bot: Bot, update: Updater, user_data: dict):

    session = Session.get_from(user_data)
    message = update.message

    if not session.connected:
        message.reply_text(
            "You must be connected to a bridge computer before sending files!")
        return ConversationHandler.END

    # Download the file
    file_object = message.document.get_file()
    download_path = f"{states.config_file.server_tmp_dir}/{message.document.file_name}"

    file_object.download(download_path)

    # Make downloaded file executable by the user
    os.chmod(download_path, 0o764)

    user_data["plugin"] = Plugin(download_path)

    view.plugin_start(user_data["plugin"].name).reply(update)

    return collect_arguments(bot, update, user_data)
Beispiel #17
0
def exclude_computers(bot: Bot, update: Updater, user_data: dict):
    """
    Change the attribute :obj:`included` for one/all computers to ``False``. If
    a computer is not included, it won't be yielded in the loop while using the
    :obj:`Computers.get_included_computers()` function.

    Note:
        The :obj:`query.data` value used by this function will be either the
        string ``exclude-all`` for all computers, or ``exclude-[mac_address]``
        for one computer.

    Args:
        bot (:obj:`telegram.bot.Bot`): The telegram bot instance.
        update (:obj:`telegram.ext.update.Updater`): The Updater associated to
            the bot.
        user_data (:obj:`dict`): The dictionary with user variables.
    """

    query = update.callback_query

    # Extract the target from the string. Values: 'all' or '[mac_address]'
    target = re.search(State.EXCLUDE_COMPUTERS, query.data).group(1)

    computers = Session.get_from(user_data).computers

    # Exclude all computers
    if target == "all":
        for computer in computers.get_computers():
            computer.included = False

    # Find the computer with the specified mac and exclude it
    else:
        computers.find(target).included = False

    # Redraw the view
    view.filter_computers(computers).edit(update)
Beispiel #18
0
def structure(bot: Bot, update: Updater, user_data: dict):
    """
    Show the nodes on the level indicated by the route from the
    :obj:`config.json` file.

    Note:
        The buttons shown by this function change dynamically depending on the
        value of ``route``.

        For example, if we have the following :obj:`config.json` file:

        .. code-block:: python

            {
                "structure": [{
                    "name": "ESIT",
                    "sections": ["Industrial", "Ingeniería"]
                },
                {
                    "name": "MATFIS"
                }]
            }

        Calling this function with :obj:`remote = []` will show the buttons
        ``ESIT`` and ``MATFIS``, but calling this function with :obj:`remote =
        ['ESIT']` will show the buttons ``Industrial`` and ``Ingeniería``, and
        calling this function with :obj:`remote = ['MATFIS']` will show no
        buttons.

    Args:
        bot (:obj:`telegram.bot.Bot`): The telegram bot instance.
        update (:obj:`telegram.ext.update.Updater`): The Updater associated to
            the bot.
        user_data (:obj:`dict`): The dictionary with user variables.
    """

    # Get the clicked section
    next_section = update.callback_query.data

    route = Session.get_from(user_data).route

    # Remove section from the route if going backwards, otherwise append
    if route and route[-1] == next_section:
        route.pop()
    else:
        route.append(next_section)

    # Return to the last section, or to the Connect menu if its the start oits
    # the start of the route
    return_to = ""
    if len(route) <= 1:
        return_to = State.CONNECT
    else:
        return_to = route[-1]

    # Update the route
    Session.get_from(user_data).route = route

    # View
    view.structure(
        route, states.config_file.get_sections(route), return_to=return_to
    ).edit(update)
Beispiel #19
0
def filter_computers(bot: Bot, update: Updater, user_data: dict):

    # View
    computers = Session.get_from(user_data).computers
    view.filter_computers(computers).edit(update)