From 91765400f6649aa0b4825e937738bbc066f4c130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=B4=E7=8C=AB?= Date: Tue, 3 Mar 2026 01:13:52 +0900 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=84=9A=E6=9C=AC=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E6=B7=BB=E5=8A=A0macOS=E6=94=AF=E6=8C=81?= =?UTF-8?q?=EF=BC=8C=E9=87=8D=E6=9E=84=E4=BE=9D=E8=B5=96=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=92=8C=E6=9C=8D=E5=8A=A1=E6=8E=A7=E5=88=B6=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/run.sh | 524 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 470 insertions(+), 54 deletions(-) diff --git a/scripts/run.sh b/scripts/run.sh index d702323a..7862e2ab 100644 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -1,7 +1,7 @@ #!/bin/bash # MaiCore & NapCat Adapter一键安装脚本 by Cookie_987 -# 适用于Arch/Ubuntu 24.10/Debian 12/CentOS 9 +# 适用于macOS/Arch/Ubuntu 24.10/Debian 12/CentOS 9 # 请小心使用任何一键脚本! INSTALLER_VERSION="0.0.5-refactor" @@ -15,36 +15,297 @@ GREEN="\e[32m" RED="\e[31m" RESET="\e[0m" -# 需要的基本软件包 - -declare -A REQUIRED_PACKAGES=( - ["common"]="git sudo python3 curl gnupg" - ["debian"]="python3-venv python3-pip build-essential" - ["ubuntu"]="python3-venv python3-pip build-essential" - ["centos"]="epel-release python3-pip python3-devel gcc gcc-c++ make" - ["arch"]="python-virtualenv python-pip base-devel" -) - -# 默认项目目录 -DEFAULT_INSTALL_DIR="/opt/maicore" +# 需要的基本软件包(兼容 Bash 3,避免使用关联数组) +REQUIRED_PACKAGES_COMMON="git sudo python3 curl gnupg" +REQUIRED_PACKAGES_DEBIAN="python3-venv python3-pip build-essential" +REQUIRED_PACKAGES_UBUNTU="python3-venv python3-pip build-essential" +REQUIRED_PACKAGES_CENTOS="epel-release python3-pip python3-devel gcc gcc-c++ make" +REQUIRED_PACKAGES_ARCH="python-virtualenv python-pip base-devel" +REQUIRED_PACKAGES_MACOS="git gnupg python" # 服务名称 SERVICE_NAME="maicore" SERVICE_NAME_WEB="maicore-web" SERVICE_NAME_NBADAPTER="maibot-napcat-adapter" +SERVICE_USER="${SUDO_USER:-$USER}" +SERVICE_HOME="$(eval echo "~${SERVICE_USER}" 2>/dev/null)" +if [[ -z "$SERVICE_HOME" || "$SERVICE_HOME" == "~${SERVICE_USER}" ]]; then + SERVICE_HOME="$HOME" +fi + +IS_MACOS=false +[[ "$(uname -s)" == "Darwin" ]] && IS_MACOS=true + +INSTALL_CONF="/etc/maicore_install.conf" + +# 默认项目目录 +DEFAULT_INSTALL_DIR="/opt/maicore" +if [[ "$IS_MACOS" == true ]]; then + DEFAULT_INSTALL_DIR="${SERVICE_HOME}/maicore" + INSTALL_CONF="${SERVICE_HOME}/.config/maicore/maicore_install.conf" +fi + +LAUNCHD_DOMAIN="" +LAUNCHD_AGENT_DIR="" +LAUNCHD_LABEL_MAIN="com.maicore.${SERVICE_NAME}" +LAUNCHD_LABEL_NBADAPTER="com.maicore.${SERVICE_NAME_NBADAPTER}" +LAUNCHD_PLIST_MAIN="" +LAUNCHD_PLIST_NBADAPTER="" + +if [[ "$IS_MACOS" == true ]]; then + SERVICE_UID="$(id -u "${SERVICE_USER}" 2>/dev/null || id -u)" + LAUNCHD_DOMAIN="gui/${SERVICE_UID}" + LAUNCHD_AGENT_DIR="${SERVICE_HOME}/Library/LaunchAgents" + LAUNCHD_PLIST_MAIN="${LAUNCHD_AGENT_DIR}/${LAUNCHD_LABEL_MAIN}.plist" + LAUNCHD_PLIST_NBADAPTER="${LAUNCHD_AGENT_DIR}/${LAUNCHD_LABEL_NBADAPTER}.plist" +fi + +get_required_packages() { + local distro="$1" + case "$distro" in + debian) + echo "${REQUIRED_PACKAGES_COMMON} ${REQUIRED_PACKAGES_DEBIAN}" + ;; + ubuntu) + echo "${REQUIRED_PACKAGES_COMMON} ${REQUIRED_PACKAGES_UBUNTU}" + ;; + centos) + echo "${REQUIRED_PACKAGES_COMMON} ${REQUIRED_PACKAGES_CENTOS}" + ;; + arch) + echo "${REQUIRED_PACKAGES_COMMON} ${REQUIRED_PACKAGES_ARCH}" + ;; + macos) + echo "${REQUIRED_PACKAGES_MACOS}" + ;; + *) + echo "${REQUIRED_PACKAGES_COMMON}" + ;; + esac +} + IS_INSTALL_NAPCAT=false IS_INSTALL_DEPENDENCIES=false +resolve_brew_bin() { + local brew_bin + brew_bin="$(command -v brew)" + [[ -z "$brew_bin" && -x /opt/homebrew/bin/brew ]] && brew_bin="/opt/homebrew/bin/brew" + [[ -z "$brew_bin" && -x /usr/local/bin/brew ]] && brew_bin="/usr/local/bin/brew" + [[ -n "$brew_bin" ]] && echo "$brew_bin" +} + +run_brew() { + local brew_bin + brew_bin="$(resolve_brew_bin)" + + [[ -z "$brew_bin" ]] && return 1 + if [[ "$(id -u)" -eq 0 && -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then + sudo -u "${SUDO_USER}" "${brew_bin}" "$@" + else + "${brew_bin}" "$@" + fi +} + +run_launchctl() { + if [[ "$(id -u)" -eq 0 && -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then + sudo -u "${SUDO_USER}" launchctl "$@" + else + launchctl "$@" + fi +} + +ensure_writable_parent() { + local path="$1" + local parent + parent="$(dirname "$path")" + mkdir -p "$parent" + if [[ "$IS_MACOS" == true && "$(id -u)" -eq 0 && -n "${SUDO_USER:-}" ]]; then + chown "${SUDO_USER}" "$parent" 2>/dev/null || true + fi +} + +save_install_info() { + ensure_writable_parent "$INSTALL_CONF" + cat > "$INSTALL_CONF" </dev/null; then + md5sum "$file_path" | awk '{print $1}' + elif command -v md5 &>/dev/null; then + md5 -q "$file_path" + else + return 1 + fi +} + +launchd_label_for_service() { + local service="$1" + case "$service" in + ${SERVICE_NAME}) + echo "$LAUNCHD_LABEL_MAIN" + ;; + ${SERVICE_NAME_NBADAPTER}) + echo "$LAUNCHD_LABEL_NBADAPTER" + ;; + *) + return 1 + ;; + esac +} + +launchd_plist_for_service() { + local service="$1" + case "$service" in + ${SERVICE_NAME}) + echo "$LAUNCHD_PLIST_MAIN" + ;; + ${SERVICE_NAME_NBADAPTER}) + echo "$LAUNCHD_PLIST_NBADAPTER" + ;; + *) + return 1 + ;; + esac +} + +is_launchd_service_loaded() { + local service="$1" + local label + label="$(launchd_label_for_service "$service")" || return 1 + run_launchctl print "${LAUNCHD_DOMAIN}/${label}" &>/dev/null +} + +start_service() { + local service="$1" + if [[ "$IS_MACOS" == true ]]; then + local label + local plist + label="$(launchd_label_for_service "$service")" || return 1 + plist="$(launchd_plist_for_service "$service")" || return 1 + if [[ ! -f "$plist" && -d "${INSTALL_DIR}/MaiBot" ]]; then + create_launchd_services + fi + if [[ ! -f "$plist" ]]; then + echo -e "${RED}未找到服务配置文件:${plist}${RESET}" + return 1 + fi + + if is_launchd_service_loaded "$service"; then + run_launchctl kickstart -k "${LAUNCHD_DOMAIN}/${label}" + else + run_launchctl bootstrap "${LAUNCHD_DOMAIN}" "$plist" + fi + else + systemctl start "$service" + fi +} + +stop_service() { + local service="$1" + if [[ "$IS_MACOS" == true ]]; then + local label + label="$(launchd_label_for_service "$service")" || return 1 + if is_launchd_service_loaded "$service"; then + run_launchctl bootout "${LAUNCHD_DOMAIN}/${label}" + fi + else + systemctl stop "$service" + fi +} + +restart_service() { + local service="$1" + if [[ "$IS_MACOS" == true ]]; then + stop_service "$service" + start_service "$service" + else + systemctl restart "$service" + fi +} + +create_launchd_services() { + mkdir -p "${LAUNCHD_AGENT_DIR}" + mkdir -p "${INSTALL_DIR}/logs" + + cat > "${LAUNCHD_PLIST_MAIN}" < + + + + Label + ${LAUNCHD_LABEL_MAIN} + ProgramArguments + + ${INSTALL_DIR}/venv/bin/python3 + bot.py + + WorkingDirectory + ${INSTALL_DIR}/MaiBot + RunAtLoad + + KeepAlive + + StandardOutPath + ${INSTALL_DIR}/logs/${SERVICE_NAME}.log + StandardErrorPath + ${INSTALL_DIR}/logs/${SERVICE_NAME}.error.log + + +EOF + + cat > "${LAUNCHD_PLIST_NBADAPTER}" < + + + + Label + ${LAUNCHD_LABEL_NBADAPTER} + ProgramArguments + + ${INSTALL_DIR}/venv/bin/python3 + main.py + + WorkingDirectory + ${INSTALL_DIR}/MaiBot-Napcat-Adapter + RunAtLoad + + KeepAlive + + StandardOutPath + ${INSTALL_DIR}/logs/${SERVICE_NAME_NBADAPTER}.log + StandardErrorPath + ${INSTALL_DIR}/logs/${SERVICE_NAME_NBADAPTER}.error.log + + +EOF + + if [[ "$(id -u)" -eq 0 && -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then + chown "${SUDO_USER}" "${LAUNCHD_PLIST_MAIN}" "${LAUNCHD_PLIST_NBADAPTER}" "${LAUNCHD_AGENT_DIR}" 2>/dev/null || true + fi +} + # 检查是否已安装 check_installed() { - [[ -f /etc/systemd/system/${SERVICE_NAME}.service ]] + if [[ "$IS_MACOS" == true ]]; then + [[ -f "$INSTALL_CONF" ]] + else + [[ -f /etc/systemd/system/${SERVICE_NAME}.service ]] + fi } # 加载安装信息 load_install_info() { - if [[ -f /etc/maicore_install.conf ]]; then - source /etc/maicore_install.conf + if [[ -f "$INSTALL_CONF" ]]; then + source "$INSTALL_CONF" else INSTALL_DIR="$DEFAULT_INSTALL_DIR" BRANCH="refactor" @@ -69,27 +330,27 @@ show_menu() { case "$choice" in 1) - systemctl start ${SERVICE_NAME} + start_service "${SERVICE_NAME}" whiptail --msgbox "✅MaiCore已启动" 10 60 ;; 2) - systemctl stop ${SERVICE_NAME} + stop_service "${SERVICE_NAME}" whiptail --msgbox "🛑MaiCore已停止" 10 60 ;; 3) - systemctl restart ${SERVICE_NAME} + restart_service "${SERVICE_NAME}" whiptail --msgbox "🔄MaiCore已重启" 10 60 ;; 4) - systemctl start ${SERVICE_NAME_NBADAPTER} + start_service "${SERVICE_NAME_NBADAPTER}" whiptail --msgbox "✅NapCat Adapter已启动" 10 60 ;; 5) - systemctl stop ${SERVICE_NAME_NBADAPTER} + stop_service "${SERVICE_NAME_NBADAPTER}" whiptail --msgbox "🛑NapCat Adapter已停止" 10 60 ;; 6) - systemctl restart ${SERVICE_NAME_NBADAPTER} + restart_service "${SERVICE_NAME_NBADAPTER}" whiptail --msgbox "🔄NapCat Adapter已重启" 10 60 ;; 7) @@ -111,7 +372,7 @@ show_menu() { # 更新依赖 update_dependencies() { whiptail --title "⚠" --msgbox "更新后请阅读教程" 10 60 - systemctl stop ${SERVICE_NAME} + stop_service "${SERVICE_NAME}" cd "${INSTALL_DIR}/MaiBot" || { whiptail --msgbox "🚫 无法进入安装目录!" 10 60 return 1 @@ -157,23 +418,23 @@ switch_branch() { whiptail --msgbox "🚫 代码拉取失败!" 10 60 return 1 fi - systemctl stop ${SERVICE_NAME} + stop_service "${SERVICE_NAME}" source "${INSTALL_DIR}/venv/bin/activate" pip install -r requirements.txt deactivate - sed -i "s/^BRANCH=.*/BRANCH=${new_branch}/" /etc/maicore_install.conf BRANCH="${new_branch}" + save_install_info check_eula whiptail --msgbox "✅ 已停止服务并切换到分支 ${new_branch} !" 10 60 } check_eula() { # 首先计算当前EULA的MD5值 - current_md5=$(md5sum "${INSTALL_DIR}/MaiBot/EULA.md" | awk '{print $1}') + current_md5=$(compute_md5 "${INSTALL_DIR}/MaiBot/EULA.md") # 首先计算当前隐私条款文件的哈希值 - current_md5_privacy=$(md5sum "${INSTALL_DIR}/MaiBot/PRIVACY.md" | awk '{print $1}') + current_md5_privacy=$(compute_md5 "${INSTALL_DIR}/MaiBot/PRIVACY.md") # 如果当前的md5值为空,则直接返回 if [[ -z $current_md5 || -z $current_md5_privacy ]]; then @@ -183,7 +444,7 @@ check_eula() { # 检查eula.confirmed文件是否存在 if [[ -f ${INSTALL_DIR}/MaiBot/eula.confirmed ]]; then # 如果存在则检查其中包含的md5与current_md5是否一致 - confirmed_md5=$(cat ${INSTALL_DIR}/MaiBot/eula.confirmed) + confirmed_md5=$(cat "${INSTALL_DIR}/MaiBot/eula.confirmed") else confirmed_md5="" fi @@ -191,7 +452,7 @@ check_eula() { # 检查privacy.confirmed文件是否存在 if [[ -f ${INSTALL_DIR}/MaiBot/privacy.confirmed ]]; then # 如果存在则检查其中包含的md5与current_md5是否一致 - confirmed_md5_privacy=$(cat ${INSTALL_DIR}/MaiBot/privacy.confirmed) + confirmed_md5_privacy=$(cat "${INSTALL_DIR}/MaiBot/privacy.confirmed") else confirmed_md5_privacy="" fi @@ -200,8 +461,8 @@ check_eula() { if [[ $current_md5 != $confirmed_md5 || $current_md5_privacy != $confirmed_md5_privacy ]]; then whiptail --title "📜 使用协议更新" --yesno "检测到MaiCore EULA或隐私条款已更新。\nhttps://github.com/MaiM-with-u/MaiBot/blob/refactor/EULA.md\nhttps://github.com/MaiM-with-u/MaiBot/blob/refactor/PRIVACY.md\n\n您是否同意上述协议? \n\n " 12 70 if [[ $? -eq 0 ]]; then - echo -n $current_md5 > ${INSTALL_DIR}/MaiBot/eula.confirmed - echo -n $current_md5_privacy > ${INSTALL_DIR}/MaiBot/privacy.confirmed + echo -n "$current_md5" > "${INSTALL_DIR}/MaiBot/eula.confirmed" + echo -n "$current_md5_privacy" > "${INSTALL_DIR}/MaiBot/privacy.confirmed" else exit 1 fi @@ -209,6 +470,98 @@ check_eula() { } +# 测速并选择PyPI源(仅当阿里云更快时使用阿里云) +measure_url_latency() { + local url="$1" + local latency + + latency=$(curl -sS -o /dev/null -w "%{time_total}" --connect-timeout 3 --max-time 8 "$url" 2>/dev/null) + + if [[ $? -eq 0 && "$latency" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then + echo "$latency" + return 0 + else + echo "999999" + return 1 + fi +} + +resolve_default_pypi_index_url() { + local default_url="" + + if [[ -n "${PIP_INDEX_URL:-}" ]]; then + default_url="$PIP_INDEX_URL" + elif [[ -n "${UV_INDEX_URL:-}" ]]; then + default_url="$UV_INDEX_URL" + elif command -v pip &>/dev/null; then + default_url=$(pip config get global.index-url 2>/dev/null | head -n 1) + if [[ -z "$default_url" ]]; then + default_url=$(pip config get install.index-url 2>/dev/null | head -n 1) + fi + fi + + if [[ -z "$default_url" ]]; then + default_url="https://pypi.org/simple" + fi + + echo "$default_url" +} + +select_pypi_index_url() { + local default_url + local aliyun_url="https://mirrors.aliyun.com/pypi/simple" + local default_latency + local aliyun_latency + local default_status + local aliyun_status + + default_url=$(resolve_default_pypi_index_url) + default_latency=$(measure_url_latency "$default_url") + default_status=$? + aliyun_latency=$(measure_url_latency "$aliyun_url") + aliyun_status=$? + + if [[ $aliyun_status -eq 0 && $default_status -ne 0 ]]; then + PYPI_INDEX_URL="$aliyun_url" + PYPI_INDEX_NAME="阿里云镜像(默认源测速失败)" + UV_PIP_INDEX_OPTION=(-i "$aliyun_url") + echo -e "${RED}默认源测速失败,已选择${PYPI_INDEX_NAME}:${PYPI_INDEX_URL}${RESET}" + return + fi + + if [[ $aliyun_status -ne 0 && $default_status -eq 0 ]]; then + PYPI_INDEX_URL="$default_url" + PYPI_INDEX_NAME="默认源(阿里云测速失败)" + UV_PIP_INDEX_OPTION=() + echo -e "${RED}阿里云测速失败,已选择${PYPI_INDEX_NAME}:不显式指定 -i 参数${RESET}" + return + fi + + if [[ $aliyun_status -ne 0 && $default_status -ne 0 ]]; then + PYPI_INDEX_URL="$default_url" + PYPI_INDEX_NAME="默认源(双源测速失败)" + UV_PIP_INDEX_OPTION=() + echo -e "${RED}默认源和阿里云测速均失败,回退到${PYPI_INDEX_NAME}:不显式指定 -i 参数${RESET}" + return + fi + + if awk "BEGIN {exit !(${aliyun_latency} < ${default_latency})}"; then + PYPI_INDEX_URL="$aliyun_url" + PYPI_INDEX_NAME="阿里云镜像" + UV_PIP_INDEX_OPTION=(-i "$aliyun_url") + else + PYPI_INDEX_URL="$default_url" + PYPI_INDEX_NAME="默认源" + UV_PIP_INDEX_OPTION=() + fi + + if [[ ${#UV_PIP_INDEX_OPTION[@]} -gt 0 ]]; then + echo -e "${GREEN}已选择${PYPI_INDEX_NAME}:${PYPI_INDEX_URL}${RESET}" + else + echo -e "${GREEN}已选择${PYPI_INDEX_NAME}:不显式指定 -i 参数${RESET}" + fi +} + # ----------- 主安装流程 ----------- run_installation() { # 1/6: 检测是否安装 whiptail @@ -221,10 +574,21 @@ run_installation() { pacman -Syu --noconfirm whiptail elif command -v yum &>/dev/null; then yum install -y whiptail + elif command -v brew &>/dev/null || [[ -x /opt/homebrew/bin/brew ]] || [[ -x /usr/local/bin/brew ]]; then + run_brew install newt + + # 确保当前 shell 能找到 Homebrew 安装的 whiptail。 + [[ -x /opt/homebrew/bin/whiptail ]] && export PATH="/opt/homebrew/bin:${PATH}" + [[ -x /usr/local/bin/whiptail ]] && export PATH="/usr/local/bin:${PATH}" else echo -e "${RED}[Error] 无受支持的包管理器,无法安装 whiptail!${RESET}" exit 1 fi + + if ! command -v whiptail &>/dev/null; then + echo -e "${RED}[Error] whiptail 安装失败或不可用,请手动安装后重试。${RESET}" + exit 1 + fi fi whiptail --title "ℹ️ 提示" --msgbox "如果您没有特殊需求,请优先使用docker方式部署。" 10 60 @@ -239,6 +603,13 @@ run_installation() { # 系统检查 check_system() { + if [[ "$IS_MACOS" == true ]]; then + ID="macos" + VERSION_ID="$(sw_vers -productVersion 2>/dev/null)" + PRETTY_NAME="macOS ${VERSION_ID}" + return + fi + if [[ "$(id -u)" -ne 0 ]]; then whiptail --title "🚫 权限不足" --msgbox "请使用 root 用户运行此脚本!\n执行方式: sudo bash $0" 10 60 exit 1 @@ -278,6 +649,9 @@ run_installation() { # 添加arch包管理器 PKG_MANAGER="pacman" ;; + macos) + PKG_MANAGER="brew" + ;; esac # 检查NapCat @@ -294,7 +668,7 @@ run_installation() { install_packages() { missing_packages=() # 检查 common 及当前系统专属依赖 - for package in ${REQUIRED_PACKAGES["common"]} ${REQUIRED_PACKAGES["$ID"]}; do + for package in $(get_required_packages "$ID"); do case "$PKG_MANAGER" in apt) dpkg -s "$package" &>/dev/null || missing_packages+=("$package") @@ -305,6 +679,22 @@ run_installation() { pacman) pacman -Qi "$package" &>/dev/null || missing_packages+=("$package") ;; + brew) + case "$package" in + git) + command -v git &>/dev/null || missing_packages+=("$package") + ;; + gnupg) + command -v gpg &>/dev/null || missing_packages+=("$package") + ;; + python) + command -v python3 &>/dev/null || missing_packages+=("$package") + ;; + *) + run_brew list --formula "$package" &>/dev/null || missing_packages+=("$package") + ;; + esac + ;; esac done @@ -327,8 +717,12 @@ run_installation() { } } - # 仅在非Arch系统上安装NapCat - [[ "$ID" != "arch" ]] && install_napcat + # 仅在 Linux 非 Arch 系统上安装 NapCat,macOS 仅支持远程 NapCat。 + if [[ "$ID" == "macos" ]]; then + whiptail --title "⚠️ NapCat 安装提示" --msgbox "当前为 macOS,暂不支持自动安装 NapCat。\n如需使用 NapCat,请配置远程实例后再连接。 " 10 60 + elif [[ "$ID" != "arch" ]]; then + install_napcat + fi # Python版本检查 check_python() { @@ -412,6 +806,9 @@ run_installation() { pacman) pacman -S --noconfirm "${missing_packages[@]}" ;; + brew) + run_brew update && run_brew install "${missing_packages[@]}" + ;; esac fi @@ -448,35 +845,42 @@ run_installation() { echo -e "${GREEN}安装Python依赖...${RESET}" + select_pypi_index_url pip install -r MaiBot/requirements.txt cd MaiBot pip install uv - uv pip install -i https://mirrors.aliyun.com/pypi/simple -r requirements.txt + uv pip install "${UV_PIP_INDEX_OPTION[@]}" -r requirements.txt cd .. echo -e "${GREEN}安装maim_message依赖...${RESET}" cd maim_message - uv pip install -i https://mirrors.aliyun.com/pypi/simple -e . + uv pip install "${UV_PIP_INDEX_OPTION[@]}" -e . cd .. echo -e "${GREEN}部署MaiBot Napcat Adapter...${RESET}" cd MaiBot-Napcat-Adapter - uv pip install -i https://mirrors.aliyun.com/pypi/simple -r requirements.txt + uv pip install "${UV_PIP_INDEX_OPTION[@]}" -r requirements.txt cd .. echo -e "${GREEN}同意协议...${RESET}" # 首先计算当前EULA的MD5值 - current_md5=$(md5sum "MaiBot/EULA.md" | awk '{print $1}') + current_md5=$(compute_md5 "MaiBot/EULA.md") # 首先计算当前隐私条款文件的哈希值 - current_md5_privacy=$(md5sum "MaiBot/PRIVACY.md" | awk '{print $1}') + current_md5_privacy=$(compute_md5 "MaiBot/PRIVACY.md") - echo -n $current_md5 > MaiBot/eula.confirmed - echo -n $current_md5_privacy > MaiBot/privacy.confirmed + echo -n "$current_md5" > MaiBot/eula.confirmed + echo -n "$current_md5_privacy" > MaiBot/privacy.confirmed - echo -e "${GREEN}创建系统服务...${RESET}" - cat > /etc/systemd/system/${SERVICE_NAME}.service </dev/null 2>&1 || true + stop_service "${SERVICE_NAME_NBADAPTER}" >/dev/null 2>&1 || true + else + echo -e "${GREEN}创建系统服务...${RESET}" + cat > /etc/systemd/system/${SERVICE_NAME}.service < /etc/systemd/system/${SERVICE_NAME_NBADAPTER}.service < /etc/systemd/system/${SERVICE_NAME_NBADAPTER}.service < /etc/maicore_install.conf - echo "INSTALL_DIR=${INSTALL_DIR}" >> /etc/maicore_install.conf - echo "BRANCH=${BRANCH}" >> /etc/maicore_install.conf + save_install_info - whiptail --title "🎉 安装完成" --msgbox "MaiCore安装完成!\n已创建系统服务:${SERVICE_NAME}、${SERVICE_NAME_WEB}、${SERVICE_NAME_NBADAPTER}\n\n使用以下命令管理服务:\n启动服务:systemctl start ${SERVICE_NAME}\n查看状态:systemctl status ${SERVICE_NAME}" 14 60 + if [[ "$IS_MACOS" == true ]]; then + whiptail --title "🎉 安装完成" --msgbox "MaiCore安装完成!\n已创建 launchctl 服务:${LAUNCHD_LABEL_MAIN}、${LAUNCHD_LABEL_NBADAPTER}\n\n首次加载:launchctl bootstrap ${LAUNCHD_DOMAIN} ${LAUNCHD_PLIST_MAIN}\n重启服务:launchctl kickstart -k ${LAUNCHD_DOMAIN}/${LAUNCHD_LABEL_MAIN}\n查看状态:launchctl print ${LAUNCHD_DOMAIN}/${LAUNCHD_LABEL_MAIN}" 14 100 + else + whiptail --title "🎉 安装完成" --msgbox "MaiCore安装完成!\n已创建系统服务:${SERVICE_NAME}、${SERVICE_NAME_WEB}、${SERVICE_NAME_NBADAPTER}\n\n使用以下命令管理服务:\n启动服务:systemctl start ${SERVICE_NAME}\n查看状态:systemctl status ${SERVICE_NAME}" 14 60 + fi } # ----------- 主执行流程 ----------- -# 检查root权限 -[[ $(id -u) -ne 0 ]] && { +# Linux 仍需 root,macOS 使用用户级 launchctl(无需 root)。 +if [[ "$IS_MACOS" == true && $(id -u) -eq 0 ]]; then + echo -e "${RED}macOS 请勿使用 root/sudo 运行此脚本,请直接以当前登录用户执行。${RESET}" + exit 1 +fi + +if [[ "$IS_MACOS" != true && $(id -u) -ne 0 ]]; then echo -e "${RED}请使用root用户运行此脚本!${RESET}" exit 1 -} +fi # 如果已安装显示菜单,并检查协议是否更新 if check_installed; then @@ -550,7 +962,11 @@ else run_installation # 安装完成后询问是否启动 if whiptail --title "安装完成" --yesno "是否立即启动MaiCore服务?" 10 60; then - systemctl start ${SERVICE_NAME} - whiptail --msgbox "✅ 服务已启动!\n使用 systemctl status ${SERVICE_NAME} 查看状态" 10 60 + start_service "${SERVICE_NAME}" + if [[ "$IS_MACOS" == true ]]; then + whiptail --msgbox "✅ 服务已启动!\n使用 launchctl print ${LAUNCHD_DOMAIN}/${LAUNCHD_LABEL_MAIN} 查看状态" 10 80 + else + whiptail --msgbox "✅ 服务已启动!\n使用 systemctl status ${SERVICE_NAME} 查看状态" 10 60 + fi fi fi