From a8b71e26f1513eba85a339408b48799305481081 Mon Sep 17 00:00:00 2001
From: Toastie <toastie@toastiet0ast.com>
Date: Mon, 24 Feb 2025 12:40:26 +1300
Subject: [PATCH] Hopefully nailed down why the e-bin script keeps on failing

---
 e-bin.sh     | 470 +++++++++++++++++++++------------------------------
 e-install.sh |  13 ++
 e-prereq.sh  | 380 +++++++++++++++++++++++++----------------
 3 files changed, 441 insertions(+), 422 deletions(-)
 create mode 100644 e-install.sh

diff --git a/e-bin.sh b/e-bin.sh
index 32b213e..bd45307 100644
--- a/e-bin.sh
+++ b/e-bin.sh
@@ -1,9 +1,4 @@
 #!/bin/bash
-#
-#
-#
-####[ Variables ]#######################################################################
-
 
 ## Colors for output
 YELLOW="$(printf '\033[0;33m')"
@@ -14,143 +9,67 @@ RED="$(printf '\033[1;31m')"
 NC="$(printf '\033[0m')"  # No color.
 readonly YELLOW GREEN BLUE CYAN RED NC
 
-## Toastielab project details
-readonly API_URL="https://toastielab.dev/api/v1/repos/EllieBotDevs/elliebot"
-
-## other constants.
+## Other constants.
 readonly BOT_EXECUTABLE="EllieBot"
-readonly BIN_DIR="elliebot-bin"
-readonly BIN_DIR_OLD="elliebot-bin.old"
-readonly CREDS_FILE="creds.yml"
-readonly CREDS_EXAMPLE_FILE="creds_example.yml"
-# Packages `ffmpeg` and `ffprobe` are highly recommended dependencies for `yt-dlp`.
-# NOTE: Do not add `python`, `python3`, or `python-is-python3` to this list, as they are
-#   checked separately.
-readonly REQUIRED_TOOLS=("jq" "yt-dlp")
-
-
-####[ Functions ]#######################################################################
-
+readonly BIN_DIR="ellie"
+readonly BACKUP_DIR="ellie_backups"
+readonly CREDS_FILE="$BIN_DIR/data/creds.yml"
+readonly CREDS_EXAMPLE_FILE="$BIN_DIR/data/creds_example.yml"
 
 ####
-# Display the main menu.
-display_menu() {
-    clear
-    echo "${BLUE}===== EllieBot Release Installer =====${NC}"
-    echo "1. Install"
-    echo "2. Run"
-    echo "3. Edit credentials"
-    echo "4. Exit"
-    echo -n "${CYAN}Enter your choice:${NC} "
-}
-
-####
-# Identify the system architecture, and return a string that represents
+# Identify the system architecture and OS, and return a string that represents it.
 #
 # RETURNS:
-#   - linux-x64: For x86_64.
-#   - linux-arm64: For aarch64 or arm64.
-#   - unsupported: For all other architectures.
+#   - linux-x64: For Linux x86_64.
+#   - linux-arm64: For Linux aarch64 or arm64.
+#   - osx-x64: For macOS x86_64.
+#   - osx-arm64: For macOS arm64.
+#   - unsupported: For all other architectures or operating systems.
 get_arch() {
-    case $(uname -m) in
-        x86_64)        echo "linux-x64" ;;
-        aarch64|arm64) echo "linux-arm64" ;;
-        *)             echo "unsupported" ;;
+    local os
+    os=$(uname -s)
+    local arch
+    arch=$(uname -m)
+
+    case "$os" in
+    Linux)
+        case "$arch" in
+        x86_64) echo "linux-x64" ;;
+        aarch64 | arm64) echo "linux-arm64" ;;
+        *) echo "unsupported" ;;
+        esac
+        ;;
+    Darwin)
+        case "$arch" in
+        x86_64) echo "osx-x64" ;;
+        arm64) echo "osx-arm64" ;;
+        *) echo "unsupported" ;;
+        esac
+        ;;
+    *)
+        echo "unsupported"
+        ;;
     esac
 }
 
-####
-# Perform a download operation using either 'wget' or 'curl'.
-#
-# NEW GLOBALS:
-#   - METHOD: The method used to download files ('wget' or 'curl').
-#
-# PARAMETERS:
-#   - $1: url (Required)
-#       - The full URL to download the item from.
-#   - $2: output_file (Optional, Default: "")
-#       - The name of the file to save the item to.
-#       - IMPORTANT: If no output file is specified, the content at the URL will be
-#           output to stdout.
-#   - $3: error_message (Optional, Default: "ERROR: Failed to download the item")
-#       - The message to display if the download fails.
-#   - $4: is_silent (Optional, Default: false)
-#       - If true, the download will be silent.
-#       - Valid Options:
-#           - true
-#           - false
-#
-# RETURNS:
-#   - 0: On successful download.
-#   - 1: On failure to download the item.
-dl() {
-    local url="$1"
-    local output_file="${2:-}"
-    local error_message="${3:-ERROR: Failed to download the item}"
-    local is_silent="${4:-false}"
-    local flags=()
-
-    ## Sets a global constant to indicate whether 'wget' or 'curl' is should be used to
-    ## download files. Additionally, set any applicable flags that are to be used.
-    if [[ -z $METHOD ]]; then
-        if hash curl &>/dev/null; then
-            METHOD="curl"
-            # Set a silent flag if the user wants to download silently.
-            [[ $is_silent == true ]] && flags+=("-s")
-            # If an output file is specified, add the '-o' flag.
-            [[ -n $output_file ]] && flags+=("-L" "-o" "$output_file")
-        elif hash wget &>/dev/null; then
-            METHOD="wget"
-            # Set a silent flag if the user wants to download silently.
-            [[ $is_silent == true ]] && flags+=("-q")
-
-            ## If an output file is specified, add the '-O' flag with the file name.
-            if [[ -n $output_file ]]; then
-                flags+=("-O" "$output_file")
-            ## If no output file is specified, add the '-O-' flag to output to stdout.
-            else
-                flags+=("-O-")
-            fi
-        else
-            echo "${RED}ERROR: Neither 'wget' nor 'curl' is installed${NC}" >&2
-            echo "${CYAN}Please install one of them and try again${NC}" >&2
-            return 1
+backup_bot() {
+    if [ -d $BIN_DIR/data/ ]; then
+        if [ ! -d $BACKUP_DIR/ ]; then
+            mkdir $BACKUP_DIR
         fi
+
+        date_now=$(date +%s)
+        cp -r $BIN_DIR/data "$BACKUP_DIR/$date_now-data"
+
+        echo "${BLUE}Your current data "
     fi
-
-    ## Download the file.
-    "$METHOD" "${flags[@]}" "$url" || {
-        echo "${RED}$error_message${NC}" >&2
-        return 1
-    }
-    return 0
-}
-
-####
-# Download EllieBot's archive.
-#
-# PARAMETERS:
-#   - $1: url (Required)
-#       - The full URL to download the archive from.
-#   - $2: output (Required)
-#       - The name of the file to save the archive to.
-#
-# RETURNS:
-#   - 0: On successful download.
-#   - 1: On failure to download the archive.
-download_archive() {
-    local url="$1"
-    local output="$2"
-
-    dl "$url" "$output" "ERROR: Failed to download the archive" || return 1
-    return 0
 }
 
 # TODO: Still needs to move data from the old directory to the new one.
 ####
 # Downloads the latest release, extracts it, and sets up EllieBot's directory. If
-# the directory already exists, it will be renamed to `elliebot-bin.old`. If
-# `elliebot-bin.old` already exists, it will be deleted.
+# the directory already exists, it will be renamed to `ellie.old`. If
+# `ellie.old` already exists, it will be deleted.
 #
 # PARAMETERS:
 #   - $1: version (Required)
@@ -158,72 +77,66 @@ download_archive() {
 #
 # RETURNS:
 #   - 1: On failure of some operation.
-install_software() {
+install_bot() {
     local version="$1"
-    local archive_dir_name
-    local archive_name="elliebot-v${version}.tar"
-    local arch; arch=$(get_arch)
-    local tar_url="${API_URL}/packages/generic/elliebot-build/${version}/${version}-${arch}-build.tar"
+    local arch
+    arch=$(get_arch)
+    local output="ellie-new"
+    local tar_url="https://toastielab.dev/EllieBotDevs/elliebot/releases/download/${version}/ellie-${arch}.tar.gz"
 
-    ## NOTE: We could move this outside of the function, such that when the script is
-    ##  executed, the architecture is determined once, then exit the script if the
-    ##  architecture is unsupported.
-    if [[ $arch == "unsupported" ]]; then
-        echo "${RED}ERROR: Unsupported architecture${NC}" >&2
-        return 1
-    fi
+    [[ $arch == "unsupported" ]] && { echo "${RED}ERROR: Unsupported architecture${NC}" >&2; return 1; }
 
     echo "${BLUE}Downloading '${version}' for '${arch}'...${NC}"
-    download_archive "$tar_url" "$archive_name" || return 1
 
-    echo "${BLUE}Extracting...${NC}"
-    tar -xf "$archive_name" || {
-        echo "${RED}ERROR: Failed to extract the release${NC}" >&2
+    [[ -d $output ]] && rm -r ./$output
+    mkdir ./$output
+
+    if ! curl -L "$tar_url" | tar -xzf - -C ./$output --strip-components=1; then
+        echo "${RED}ERROR: Failed to download or extract the archive${NC}" >&2
+        rm -r ./$output
         return 1
-    }
-
-    archive_dir_name=$(tar -tf "$archive_name" | head -1 | cut -f1 -d"/")
-
-    ## If `elliebot-bin.old` already exists, delete it, so that we can rename the
-    ## current `elliebot-bin` to it.
-    if [[ -d $BIN_DIR_OLD ]]; then
-        echo "${BLUE}Removing '$BIN_DIR_OLD'...${NC}"
-        rm -rf "$BIN_DIR_OLD"
     fi
 
-    ## If the directory already exists, rename it to `elliebot-bin.old`.
     if [[ -d $BIN_DIR ]]; then
-        echo "${BLUE}Renaming '$BIN_DIR' to '$BIN_DIR_OLD'...${NC}"
-        mv "$BIN_DIR" "$BIN_DIR_OLD"
-    fi
-
-    echo "${BLUE}Renaming '$archive_dir_name' to '$BIN_DIR'...${NC}"
-    if [[ -d $archive_dir_name ]]; then
-        mv "$archive_dir_name" "$BIN_DIR"
+        backup_bot
+        mv "$BIN_DIR" "${BIN_DIR}-old"
     else
-        echo "${RED}ERROR: Unarchived directory '$archive_dir_name' not found${NC}" >&2
-        return 1
+        echo "${BLUE}EllieBot not installed. Installing for the first time.${NC}"
+    fi
+
+    mv ./$output $BIN_DIR
+
+    if [[ -d "${BIN_DIR}-old" ]]; then
+        echo "${BLUE}Copying over data folder...${NC}"
+        [[ -d "${BIN_DIR}-old/data/" ]] && cp -rf "${BIN_DIR}-old/data/"* "$BIN_DIR/data/"
     fi
 
-    rm "$archive_name"
     chmod +x "${BIN_DIR}/${BOT_EXECUTABLE}"
     echo "${GREEN}Installation complete!${NC}"
+
+    # Clean up any leftover folders
+    [[ -d $output ]] && rm -r ./$output
+    [[ -d "${BIN_DIR}-old" ]] && rm -r "${BIN_DIR}-old"
 }
 
+# TODO: Somehow get the bot version, and
+#       paint available versions with color
+#       RED: older (downgrade), not recommended
+#       GREEN: newer (upgrade)
+#       BLUE: same (reinstall)
+
 ####
 # Display a list of available versions, and prompt the user to select one to install.
 install_submenu() {
     local versions
-    # NOTE: This works because the `jq` command outputs a newline-separated list.
-    mapfile -t versions < <(
-        dl "${API_URL}/releases" "" "ERROR: Failed to get releases" \
-            true | jq -r '.[].tag_name' | sort -V -r
-    )
+
+    # get versions from /refs/tags github endpoint
+    mapfile -t versions < <(curl -s https://toastielab.dev/api/v1/repos/EllieBotDevs/elliebot/git/refs/tags | grep -oP '"ref": "refs/tags/\K[^"]+')
 
     echo "${CYAN}Select version to install:${NC}"
     select version in "${versions[@]}"; do
         if [[ -n $version ]]; then
-            install_software "$version"
+            install_bot "$version"
             break
         else
             echo "${RED}ERROR: Invalid selection${NC}"
@@ -232,25 +145,23 @@ install_submenu() {
 }
 
 ####
-# Check if the 'token' in 'creds.yml' is set.
+# Determines whether the 'token' field in the credentials file is set.
 #
-# NOTE: This function is not foolproof, as it only checks if the 'token' is set to an
-#   empty string. It does not check if the token is valid.
+# NOTE:
+#   This is not a comprehensive check for the validity of the token; it only verifies
+#   that the token field is not empty.
 #
 # RETURNS:
-#   - 0: If the 'token' is not set.
-#   - 1: If the 'token' is set.
+#   - 0: If the token is set.
+#   - 1: If the token is not set.
 is_token_set() {
-    if [[ ! -f $BIN_DIR/$CREDS_FILE ]]; then
-        return 0
-    elif grep -Eq '^token: '\'\''' "$BIN_DIR/$CREDS_FILE"; then
+    if grep -Eq '^token: '\'\''' "$CREDS_FILE"; then
         return 1
     else
         return 0
     fi
 }
 
-
 ####
 # Verify that the bot is installed, the token in the 'creds.yml' file is set, and then
 # run the bot.
@@ -272,14 +183,17 @@ run_bot() {
     fi
 
     ## Create the creds file if it doesn't exist.
-    if [[ ! -f $BIN_DIR/$CREDS_FILE ]]; then
-        cp -f "$BIN_DIR/$CREDS_EXAMPLE_FILE" "$BIN_DIR/$CREDS_FILE"
+    if [[ ! -f $CREDS_FILE ]]; then
+        if [[ ! -f $CREDS_EXAMPLE_FILE ]]; then
+            echo "${RED}ERROR: 'creds_xample.yml'not found. Make sure the bot is installed${NC}" >&2
+            return 1
+        fi
+        cp -f "$CREDS_EXAMPLE_FILE" "$CREDS_FILE"
     fi
 
-    ## Ensure that the token is set. Do note that it won't say if the token is invalid.
-    if ! is_token_set; then
-        echo "${YELLOW}WARNING: 'token' is not set in '$CREDS_FILE'. Please add your" \
-            "token and try again.${NC}" >&2
+    ## Ensure that the token is set in the creds file or check if env var bot_token is set
+    if ! is_token_set && [[ -z "${bot_token}" ]]; then
+        echo "${RED}ERROR: Bot token not set. Please set it in the credentials file or as an environment variable.${NC}" >&2
         return 1
     fi
 
@@ -289,114 +203,116 @@ run_bot() {
     popd >/dev/null || return 1
 }
 
-####
-# Verify that Python 3 is installed, and that the version is 3.9 or higher.
-#
-# RETURNS:
-#   - 0: If the verification passes.
-#   - 1: If the verification fails.
-python_verification() {
-    local verification_passed=true
+install_music_dependencies() {
+    curl -L -o e-musicreq.sh https://toastielab.dev/EllieBotDevs/ellie-bash-installer/raw/branch/v5/e-prereq.sh || {
+        echo "ERROR: Failed to download the music dependencies installer"
+        return 1
+    }
 
-    ## Verify that Python 3 is installed.
-    if ! hash python3 &>/dev/null; then
-        echo "${YELLOW}WARNING: Python 3 is not installed${NC}" >&2
-        verification_passed=false
-    fi
-
-    ## Some systems, specifically Linux distributions like Ubuntu, don't have `python`
-    ## aliased to `python3`. This check is to ensure that `python` is available.
-    if ! hash python &>/dev/null; then
-        echo "${YELLOW}WARNING: The 'python' command is not available${NC}" >&2
-        echo "${CYAN}You may need to install 'python-is-python3'${NC}" >&2
-        verification_passed=false
-    fi
-
-    ## Some systems still have python2 available and installed. We need to ensure that
-    ## the version of python is 3.9 or higher.
-    if ! python -c "import sys; assert sys.version_info >= (3, 9)" &>/dev/null; then
-        echo "${YELLOW}WARNING: Python 3.9 or higher is required${NC}" >&2
-        verification_passed=false
-    fi
-
-    [[ $verification_passed == false ]] && return 1
-    return 0
-}
-
-####
-# Check if external tools that often need to be installed are available. If any are
-# missing, print an error message and exit.
-#
-# EXITS:
-#   - 1: If any of the required tools are not installed.
-verify_tools() {
-    local all_tools_installed=true
-
-    for tool in "${REQUIRED_TOOLS[@]}"; do
-        if ! hash "$tool" &>/dev/null; then
-            all_tools_installed=false
-            echo "${YELLOW}WARNING: '$tool' is not installed${NC}" >&2
-        fi
-    done
-
-    python_verification
-
-    if [[ $all_tools_installed == false ]]; then
-        echo "${CYAN}Please install the required tools and try again${NC}" >&2
-        exit 1
-    fi
-}
-
-# TODO: Still needs to be edited/expanded upon.
-edit_creds() {
-    local creds_file="${BIN_DIR}/creds.yml"
-
-    if [[ ! -d $BIN_DIR ]]; then
-	echo "Please install the bot first."
-	return 1
-    fi
-
-    if [[ ! -f $creds_file ]]; then
-	cp $BIN_DIR/creds_example.yml $creds_file
-    fi
-
-    local file="$1"
-    local editors=("nano" "micro" "code" "vim" "emacs" "gedit")
-
-    if [ ! -f "$file" ]; then
-        echo "ERROR: File '$file' does not exist." >&2
+    if [[ ! -f e-musicreq.sh ]]; then
+        echo "${RED}ERROR: Failed to download the music dependencies installer${NC}" >&2
         return 1
     fi
 
-    for editor in "${editors[@]}"; do
-        if command -v "$editor" >/dev/null 2>&1; then
-            "$editor" "$file"
-            return 0
-        fi
-    done
-
-    echo "ERROR: No known text editors (${editors[*]}) are available on this system." >&2
-    return 1
+    chmod +x e-musicreq.sh
+    ./e-musicreq.sh
+    rm e-musicreq.sh
 }
 
+edit_creds() {
+    if [[ ! -f $CREDS_FILE ]]; then
+        cp $CREDS_EXAMPLE_FILE $CREDS_FILE
+    fi
 
-####[ Main ]############################################################################
+    # ask the user to input the token
+    echo "Please input your token: "
+    echo ""
+    read -r token
 
+    # check if the token is not empty
+    if [[ -z "$token" ]]; then
+        echo "ERROR: Invalid token." >&2
+        return 1
+    fi
 
-verify_tools
+    # replace the token in the creds file
+    # by finding a line which starts with 'token: ' and replacing it
+    sed -i "s/token: .*/token: \"$token\"/" $CREDS_FILE
+}
+
+migrate_from_v5() {
+
+    # 0. Ensure that the bot is installed
+    if [[ ! -d $BIN_DIR ]]; then
+        echo "${RED}ERROR: EllieBot v6 not installed. Please install it first and then you'll be able to migrate.${NC}"
+        return 1
+    fi
+
+    # 1. Check if there is a elliebot/output folder
+    if [ ! -d "elliebot/output" ]; then
+        echo "${RED}ERROR: No v5 installation found.${NC}"
+        return 1
+    fi
+
+    # 2. Copy over the entirety of the data folder and overwrite
+    if [ -d "elliebot/output/data/" ]; then
+        # strings are no longer there, nor do we need them
+        rm -rf elliebot/output/data/strings
+        cp -rf elliebot/output/data/* ellie/data/
+
+        # move the data folder to avoid double migration
+        mv elliebot/output/data elliebot/output/data.old
+    fi
+
+    # Done
+    echo "${GREEN}Migration successful! Your v6 bot is now in the 'ellie' folder.${NC}"
+    MIGRATED=1
+}
+
+####
+# Display the main menu options.
+display_menu() {
+    clear
+    echo "${BLUE}===== EllieBot Release Installer =====${NC}"
+    echo "1. Install"
+    echo "2. Run"
+    echo "3. Add Token"
+    echo "4. Install music dependencies"
+    echo "5. Exit"
+    if [[ ! $MIGRATED -eq 1 && -d "elliebot/output/data" ]]; then
+        echo "6. Migrate v5 from-source version"
+    fi
+    echo -n "${CYAN}Enter your choice:${NC} "
+}
+
+MIGRATED=1
+# check if there is a elliebot/output/data folder
+if [ ! -d "elliebot/output/data" ]; then
+    MIGRATED=0
+fi
 
 while true; do
     display_menu
     read -r choice
-
     case $choice in
-        1) install_submenu ;;
-        2) run_bot ;;
-        3) edit_creds ;;
-        4) echo "${GREEN}Exiting...${NC}"; exit 0 ;;
-        *) echo "${RED}ERROR: Invalid option${NC}" ;;
+    1) install_submenu ;;
+    2) run_bot ;;
+    3) edit_creds ;;
+    4) install_music_dependencies ;;
+    5)
+        echo "${GREEN}Exiting...${NC}"
+        exit 0
+        ;;
+    6)
+        if [[ $MIGRATED -eq 1 || ! -d "elliebot/output/data" ]]; then
+            echo "${YELLOW}WARNING: Nothing to migrate. You must have a v5 elliebot/output folder!${NC}" >&2
+            break
+        fi
+        migrate_from_v5
+        ;;
+    *) echo "${RED}ERROR: Invalid option${NC}" ;;
     esac
 
-    echo "${CYAN}Press [Enter] to continue...${NC}"
+    echo "${CYAN}Press [Enter] to continue${NC}"
     read -r
-done
+done
\ No newline at end of file
diff --git a/e-install.sh b/e-install.sh
new file mode 100644
index 0000000..66971f9
--- /dev/null
+++ b/e-install.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+echo ""
+echo "Welcome to EllieBot."
+echo "Downloading the latest installer..."
+root=$(pwd)
+
+rm "$root/e-bin.sh" 1>/dev/null 2>&1
+curl -L -o "$root/e-bin.sh" https://toastielab.dev/EllieBotDevs/ellie-bash-installer/raw/branch/v6/e-bin.sh
+
+bash e-bin.sh
+cd "$root"
+rm "$root/e-bin.sh"
+exit 0
\ No newline at end of file
diff --git a/e-prereq.sh b/e-prereq.sh
index 1dd2b09..052b6f7 100644
--- a/e-prereq.sh
+++ b/e-prereq.sh
@@ -1,163 +1,253 @@
-#!/bin/bash -e
-# Install dotnet
-root=$(pwd)
-echo ""
+#!/bin/bash
 
-function INSTALL_YTDLP {
-    wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O ~/.local/bin/yt-dlp
-    chmod a+rx ~/.local/bin/yt-dlp
+if [ "$EUID" -ne 0 ]; then
+  sudo_cmd="sudo "
+fi
+
+declare -A -r APT_CMD=(
+    ["update"]="${sudo_cmd}apt update"
+    ["install"]="${sudo_cmd}apt install -y"
+)
+
+declare -A -r DNF_CMD=(
+    ["update"]="${sudo_cmd}dnf update -y"
+    ["install"]="${sudo_cmd}dnf install -y"
+)
+
+declare -A -r ZYPPER_CMD=(
+    ["update"]="${sudo_cmd}zypper refresh"
+    ["install"]="${sudo_cmd}zypper install -y"
+)
+
+declare -A -r BREW_CMD=(
+    ["update"]="${sudo_cmd}brew update"
+    ["install"]="${sudo_cmd}brew install"
+)
+
+declare -A -r PACMAN_CMD=(
+    ["update"]="${sudo_cmd}pacman -Sy"
+    ["install"]="${sudo_cmd}pacman -Sy --noconfirm"
+)
+
+readonly YT_DLP_PATH="$HOME/.local/bin/yt-dlp"
+
+####
+# Identify the system's distribution, version, and architecture.
+#
+# NOTE:
+#   The 'os-release' file is used to determine the distribution and version. This file
+#   is present on almost every distributions running systemd.
+#
+# NEW GLOBALS:
+#   - DISTRO: The distribution name.
+#   - VER: The distribution version.
+#   - SVER: The distribution version without the minor version.
+#   - ARCH: The system architecture.
+#   - BITS: The system architecture in bits.
+detect_sys_info() {
+    if [[ $(uname -s) == "Darwin" ]]; then
+        DISTRO="darwin"
+        VER=$(sw_vers -productVersion)
+    elif [[ -f /etc/os-release ]]; then
+        . /etc/os-release
+        DISTRO="$ID"
+        VER="$VERSION_ID"  # Version: x.x.x...
+        SVER=${VER//.*/}  # Version: x
+    else
+        DISTRO=$(uname -s)
+        VER=$(uname -r)
+    fi
+
+    case $(uname -m) in
+        x86_64)  BITS="64"; ARCH="x64" ;;
+        aarch64) BITS="64"; ARCH="arm64" ;;
+        armv*)   BITS="32"; ARCH="arm32" ;;  # Generic ARM 32-bit.
+        i*86)    BITS="32"; ARCH="x86" ;;
+        *)       BITS="?";  ARCH="$(uname -m)" ;;  # Fallback to uname output.
+    esac
 }
 
-wget -q -N https://toastielab.dev/EllieBotDevs/ellie-bash-installer/raw/branch/v5/detectOS.sh
-declare DETECTED=($(bash detectOS.sh))
+####
+# Install 'yt-dlp' at '~/.local/bin/yt-dlp'.
+#
+# EXITS:
+#   - 1: Failed to download 'yt-dlp'.
+install_yt_dlp() {
 
-if [[ ${DETECTED[0]} = "" ]]; then exit 1; fi
-
-OS="${DETECTED[0]}"
-VER="${DETECTED[1]}"
-ARCH="${DETECTED[2]}"
-SVER="${DETECTED[3]}"
-
-echo "This installer will download all of the required packages for EllieBot. It will use about 350MB of space. This might take awhile to download if you do not have a good internet connection.\n"
-echo -e "Would you like to continue? \nYour OS: $OS \nOS Version: $VER \nArchitecture: $ARCH"
-
-while true; do
-    read -p "[y/n]: " yn
-    case $yn in
-        [Yy]* ) clear; echo Running EllieBot Auto-Installer; sleep 2; break;;
-        [Nn]* ) echo Quitting...; rm e-prereq.sh && exit;;
-        * ) echo "Couldn't get that please type [y] for Yes or [n] for No.";;
-    esac
-done
-
-echo ""
-
-if [ "$OS" = "Ubuntu" ]; then
-    if [ "$VER" = "23.10" ]; then
-        echo -e "*Ubuntu 23.10 will reach End Of Life (EOL) on July 01, 2024. For more information, see the official Ubuntu EOL page. "
+    if command -v yt-dlp &>/dev/null; then
+        echo "${BLUE}Updating 'yt-dlp'...${NC}"
+        yt-dlp -U || {
+            echo "${RED}Failed to update 'yt-dlp'${NC}" >&2
+        }
+        return
     fi
-    echo "Installing dotnet"
-    wget "https://packages.microsoft.com/config/ubuntu/$VER/packages-microsoft-prod.deb" -O packages-microsoft-prod.deb
-    sudo dpkg -i packages-microsoft-prod.deb
-    rm packages-microsoft-prod.deb
 
-    sudo apt-get update;
-    sudo apt-get install -y apt-transport-https && sudo apt-get update;
-    sudo apt-get install -y dotnet-sdk-8.0;
+    # get correct yt-dlp based on arch
+    local yt_dlp_url="https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux"
+    case $ARCH in
+        x64) yt_dlp_url="${yt_dlp_url}" ;;
+        arm64) yt_dlp_url="${yt_dlp_url}_aarch64" ;;
+        arm32) yt_dlp_url="${yt_dlp_url}_armv7l" ;;
+        *) echo "${RED}Unsupported architecture: $ARCH${NC}" >&2; return 1 ;;
+    esac
 
-    echo "Installing Git and Tmux..."
-    sudo apt-get install git tmux -y
+    [[ ! -d "$HOME/.local/bin" ]] && mkdir -p "$HOME/.local/bin"
 
-    echo "Installing music prerequisites..."
-    sudo apt-get install libopus0 opus-tools libopus-dev libsodium-dev python ffmpeg -y
-    echo ""
-    INSTALL_YTDLP
+    if [[ ! -f $YT_DLP_PATH ]]; then
+        echo "${BLUE}Installing 'yt-dlp'...${NC}"
+        curl -L "$yt_dlp_url" -o "$YT_DLP_PATH" || {
+            echo "${RED}Failed to download 'yt-dlp'${NC}" >&2
+            return 1
+        }
+    fi
 
-elif [ "$OS" = "Debian" ]; then
-    if [[ "$SVER" == "9" ]]; then
-        echo "Support for Debian 9 has reached End of Life (EOL) as of August 9, 2022"
-        echo "Please upgrade to Debian 10 or newer"
-        rm e-prereq.sh
+    echo "${BLUE}Modifying permissions for 'yt-dlp'...${NC}"
+    chmod a+rx "$YT_DLP_PATH"
+}
+
+####
+# Installs music prerequisites.
+#
+# PARAMETERS:
+#   - $1: cmd_array (Required)
+#       - The associative array containing the package manager commands.
+#   - $2: pkg_list (Required)
+#       - The array containing the list of packages to install.
+install_music_prereqs() {
+    local -n cmd_array="$1"
+    local -n pkg_list="$2"
+
+    echo -e "${YELLOW}About to run: ${cmd_array["update"]}${NC}"
+    read -p "${CYAN}Continue? [y/N]: ${NC}" -n 1 -r
+    echo
+    if [[ $REPLY =~ ^[Yy]$ ]]; then
+        ${cmd_array["update"]} || {
+            echo "${RED}Failed to update${NC}" >&2
+            exit 1
+        }
+    else
+        echo "${RED}Update skipped${NC}"
+    fi
+
+
+    echo -e "${YELLOW}About to run: ${cmd_array["install"]} ${pkg_list[*]}${NC}"
+    read -p "${CYAN}Continue? [y/N]: ${NC}" -n 1 -r
+    echo
+    if [[ $REPLY =~ ^[Yy]$ ]]; then
+        ${cmd_array["install"]} "${pkg_list[@]}" || {
+            echo "${RED}Failed to install music prerequisites${NC}" >&2
+            exit 1
+        }
+    else
+        echo "${RED}Installation cancelled${NC}"
         exit 1
     fi
 
-    echo "Installing dotnet..."
-    wget https://packages.microsoft.com/config/debian/"$SVER"/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
-    sudo dpkg -i packages-microsoft-prod.deb
-    rm packages-microsoft-prod.deb
+    install_yt_dlp
 
-    sudo apt-get update; \
-      sudo apt-get install -y apt-transport-https && \
-      sudo apt-get update && \
-      sudo apt-get install -y dotnet-sdk-8.0
+    echo -e "\n${GREEN}Finished installing music prerequisites${NC}"
+}
 
-    echo "Installing Git and Tmux..."
-    sudo apt-get install git tmux -y
+detect_sys_info
 
-    echo "Installing music prerequisites..."
-    sudo apt-get install libopus0 libopus-dev libsodium-dev ffmpeg -y
-    echo ""
-    INSTALL_YTDLP
-
-elif [ "$OS" = "Fedora" ]; then
-    sudo dnf -y install dotnet-sdk-8.0
-    sudo dnf -y install git tmux
-
-    sudo dnf -y install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
-    sudo dnf -y install ffmpeg
-    sudo dnf -y install opus-tools opus libsodium
-    INSTALL_YTDLP
-
-elif [ "$OS" = "openSUSE Leap" ] || [ "$OS" = "openSUSE Tumbleweed" ]; then
-    echo -e "Installing dotnet..."
-    sudo zypper install -y libicu wget
-    sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
-    wget https://packages.microsoft.com/config/opensuse/15/prod.repo
-    sudo mv prod.repo /etc/zypp/repos.d/microsoft-prod.repo
-    sudo chown root:root /etc/zypp/repos.d/microsoft-prod.repo
-    sudo zypper install -y dotnet-sdk-8.0
-
-    echo -e "\nInstalling git, tmux..."
-    sudo zypper install -y git tmux
-
-    echo -e "\nInstalling music prerequisites..."
-    sudo zypper install -y ffmpeg libopus0 yt-dlp
-
-elif [ "$OS" = "LinuxMint" ]; then
-    echo "Installing Git and Tmux..."
-    sudo apt-get update;
-    sudo apt-get install -y git tmux
-
-    echo "Installing dotnet..."
-    if [ "$SVER" = "19" ] || [ "$SVER" = "20" ]; then
-        wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
-    elif [ "$SVER" = "21" ]; then
-        wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
-    fi
-
-    sudo dpkg -i packages-microsoft-prod.deb
-    rm packages-microsoft-prod.deb
-    sudo apt-get update && \
-      sudo apt-get install -y dotnet-sdk-8.0
-
-    sudo apt-get install -y apt-transport-https && \
-      sudo apt-get update;
-
-    echo "Installing music prerequisites..."
-    sudo apt-get install -y libopus0 opus-tools libopus-dev libsodium-dev ffmpeg
-    INSTALL_YTDLP
-
-elif [ "$OS" = "AlmaLinux" ] || [ "$OS" = "Rocky Linux" ]; then
-    echo "Installing dotnet..."
-    sudo dnf install -y dotnet-sdk-8.0
-
-    echo "Installing Git and Tmux..."
-    sudo dnf install -y wget git opus tmux python3.11
-
-    echo "Installing music prerequisites..."
-
-    if [ "$SVER" = "8" ]; then
-        sudo dnf -y install https://download.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
-        sudo yum install yum-utils -y
-        sudo yum-config-manager --enable powertools
-    fi
-
-    sudo dnf install -y --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm
-    sudo dnf install -y ffmpeg
-    INSTALL_YTDLP
-
-elif [ "$OS" = "Darwin" ]; then
-    brew update
-    brew install wget git ffmpeg openssl opus opus-tools opusfile libffi libsodium tmux python yt-dlp
-
-    brew install mono-libgdiplus
+if [[ $BITS == "32" ]]; then
+    echo "${RED}Your system is 32-bit, which is unsupported${NC}" >&2
+    exit 1
 fi
 
-echo
-echo "EllieBot Prerequisites Installation completed..."
-read -n 1 -s -p "Press any key to continue..."
-sleep 2
+case "$DISTRO" in
+    ubuntu)
+        readonly pkgs=(ffmpeg curl)
+        case "$VER" in
+            22.04|24.04) install_music_prereqs APT_CMD pkgs ;;
+            *) unsupported ;;
+        esac
+        ;;
+    debian)
+        readonly pkgs=(ffmpeg curl)
+        case "$SVER" in
+            1[1-3]) install_music_prereqs APT_CMD pkgs ;;
+            *) unsupported ;;
+        esac
+        ;;
+    linuxmint)
+        readonly pkgs=(ffmpeg curl)
+        case "$SVER" in
+            2[0-3]) install_music_prereqs APT_CMD pkgs ;;
+            *) unsupported ;;
+        esac
+        ;;
+    fedora)
+        readonly pkgs=(ffmpeg-free curl)
+        case "$SVER" in
+            38|39|40|41|42|43) install_music_prereqs DNF_CMD pkgs ;;
+            *) unsupported ;;
+        esac
+        ;;
+    almalinux|rocky)
+        el_ver=$(rpm -E %rhel)
 
-cd "$root"
-rm "$root/e-prereq.sh"
-exit 0
+        # add rpm fusion repo
+        repo_cmd=(
+            "${sudo_cmd}dnf install -y epel-release"
+            "sudo dnf install --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-${el_ver}.noarch.rpm -y"
+        )
+
+        if (( el_ver == 8 )); then
+            repo_cmd+=("${sudo_cmd}dnf config-manager --set-enabled powertools")
+        elif (( el_ver == 9 )); then
+            repo_cmd+=("${sudo_cmd}dnf config-manager --set-enabled crb")
+        fi
+
+        echo "${YELLOW}In order to install ffmpeg, you need to enable the RPM Fusion repository.${NC}"
+        echo "${YELLOW}The following commands will be run:${NC}"
+        for cmd in "${repo_cmd[@]}"; do
+            echo "  ${YELLOW}-> $cmd${NC}"
+        done
+
+        # ask the user whether it's ok to run this command
+        read -p "${CYAN}Do you want to continue with these commands? [y/N]: ${NC}" -n 1 -r
+        echo ""
+        if [[ $REPLY =~ ^[Yy]$ ]]; then
+            for cmd in "${repo_cmd[@]}"; do
+                echo "${BLUE}Running: $cmd${NC}"
+                $cmd || {
+                    echo "${RED}Failed to run: $cmd${NC}" >&2
+                    exit 1
+                }
+            done
+        else
+            echo "${RED}User opted to cancel the command execution.${NC}" >&2
+            exit 1
+        fi
+
+        readonly pkgs=(ffmpeg curl)
+        install_music_prereqs DNF_CMD pkgs
+        ;;
+    opensuse-leap)
+        readonly pkgs=(ffmpeg curl)
+        case "$VER" in
+            15.5|15.6|15.7) install_music_prereqs ZYPPER_CMD pkgs ;;
+            *) unsupported ;;
+        esac
+        ;;
+    opensuse-tumbleweed)
+        readonly pkgs=(ffmpeg curl)
+        install_music_prereqs ZYPPER_CMD pkgs ;;
+    arch|artix)
+        readonly pkgs=(ffmpeg pipewire-jack curl)
+        install_music_prereqs PACMAN_CMD pkgs
+        ;;
+    darwin)
+        if ! command -v brew &>/dev/null; then
+            echo "${RED}Homebrew is not installed. Please install Homebrew first.${NC}" >&2
+            exit 1
+        fi
+        readonly pkgs=(ffmpeg python curl)
+        install_music_prereqs BREW_CMD pkgs
+        ;;
+    *)
+        echo "${RED}Unsupported distribution: $DISTRO${NC}" >&2
+        exit 1
+        ;;
+esac
\ No newline at end of file