refactor: simplify run.sh by extracting shared logic and eliminating duplication

This commit is contained in:
zhayujie
2026-03-19 11:07:16 +08:00
parent 9192f6f7f7
commit 49d8707c58

603
run.sh
View File

@@ -9,7 +9,6 @@ set -e
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
YELLOW='\033[0;33m' YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m' CYAN='\033[0;36m'
BOLD='\033[1m' BOLD='\033[1m'
NC='\033[0m' NC='\033[0m'
@@ -159,20 +158,18 @@ clone_project() {
if ! command -v git &> /dev/null; then if ! command -v git &> /dev/null; then
echo -e "${YELLOW}⚠️ Git not available. Trying wget/curl...${NC}" echo -e "${YELLOW}⚠️ Git not available. Trying wget/curl...${NC}"
local zip_url="https://gitee.com/zhayujie/chatgpt-on-wechat/repository/archive/master.zip"
if command -v wget &> /dev/null; then if command -v wget &> /dev/null; then
wget https://gitee.com/zhayujie/chatgpt-on-wechat/repository/archive/master.zip -O chatgpt-on-wechat.zip wget "$zip_url" -O chatgpt-on-wechat.zip
unzip chatgpt-on-wechat.zip
mv chatgpt-on-wechat-master chatgpt-on-wechat
rm chatgpt-on-wechat.zip
elif command -v curl &> /dev/null; then elif command -v curl &> /dev/null; then
curl -L https://gitee.com/zhayujie/chatgpt-on-wechat/repository/archive/master.zip -o chatgpt-on-wechat.zip curl -L "$zip_url" -o chatgpt-on-wechat.zip
unzip chatgpt-on-wechat.zip
mv chatgpt-on-wechat-master chatgpt-on-wechat
rm chatgpt-on-wechat.zip
else else
echo -e "${RED}❌ Cannot download project. Please install Git, wget, or curl.${NC}" echo -e "${RED}❌ Cannot download project. Please install Git, wget, or curl.${NC}"
exit 1 exit 1
fi fi
unzip chatgpt-on-wechat.zip
mv chatgpt-on-wechat-master chatgpt-on-wechat
rm chatgpt-on-wechat.zip
else else
git clone https://github.com/zhayujie/chatgpt-on-wechat.git || \ git clone https://github.com/zhayujie/chatgpt-on-wechat.git || \
git clone https://gitee.com/zhayujie/chatgpt-on-wechat.git git clone https://gitee.com/zhayujie/chatgpt-on-wechat.git
@@ -198,69 +195,52 @@ clone_project() {
# Install dependencies # Install dependencies
install_dependencies() { install_dependencies() {
echo -e "${GREEN}📦 Installing dependencies...${NC}" echo -e "${GREEN}📦 Installing dependencies...${NC}"
local PIP_MIRROR="-i https://pypi.tuna.tsinghua.edu.cn/simple"
# For Python 3.11+, use --break-system-packages to avoid externally-managed-environment errors
PIP_EXTRA_ARGS="" PIP_EXTRA_ARGS=""
if $PYTHON_CMD -c "import sys; exit(0 if sys.version_info >= (3, 11) else 1)" 2>/dev/null; then if $PYTHON_CMD -c "import sys; exit(0 if sys.version_info >= (3, 11) else 1)" 2>/dev/null; then
PIP_EXTRA_ARGS="--break-system-packages" PIP_EXTRA_ARGS="--break-system-packages"
echo -e "${YELLOW}Python 3.11+ detected, using --break-system-packages for pip installations${NC}" echo -e "${YELLOW}Python 3.11+ detected, using --break-system-packages for pip installations${NC}"
fi fi
# Upgrade pip and basic tools (ignore existing system packages to avoid conflicts)
echo -e "${YELLOW}Upgrading pip and basic tools...${NC}" echo -e "${YELLOW}Upgrading pip and basic tools...${NC}"
set +e set +e
$PYTHON_CMD -m pip install --upgrade pip setuptools wheel importlib_metadata --ignore-installed $PIP_EXTRA_ARGS -i https://pypi.tuna.tsinghua.edu.cn/simple > /tmp/pip_upgrade.log 2>&1 $PYTHON_CMD -m pip install --upgrade pip setuptools wheel importlib_metadata --ignore-installed $PIP_EXTRA_ARGS $PIP_MIRROR > /tmp/pip_upgrade.log 2>&1
if [ $? -ne 0 ]; then [ $? -ne 0 ] && echo -e "${YELLOW}⚠️ Some tools failed to upgrade, but continuing...${NC}"
echo -e "${YELLOW}⚠️ Some tools failed to upgrade, but continuing...${NC}"
cat /tmp/pip_upgrade.log | head -20
fi
set -e set -e
rm -f /tmp/pip_upgrade.log rm -f /tmp/pip_upgrade.log
# Common packages that may have distutils/system conflicts
COMMON_CONFLICT_PACKAGES="PyYAML setuptools wheel certifi charset-normalizer"
# Try normal installation first
echo -e "${YELLOW}Installing project dependencies...${NC}" echo -e "${YELLOW}Installing project dependencies...${NC}"
set +e
# Save output and capture exit code correctly $PYTHON_CMD -m pip install -r requirements.txt $PIP_EXTRA_ARGS $PIP_MIRROR > /tmp/pip_install.log 2>&1
set +e # Temporarily disable exit on error local exit_code=$?
$PYTHON_CMD -m pip install -r requirements.txt $PIP_EXTRA_ARGS -i https://pypi.tuna.tsinghua.edu.cn/simple > /tmp/pip_install.log 2>&1 set -e
INSTALL_EXIT_CODE=$?
set -e # Re-enable exit on error
# Show output
cat /tmp/pip_install.log cat /tmp/pip_install.log
if [ $INSTALL_EXIT_CODE -eq 0 ]; then if [ $exit_code -eq 0 ]; then
echo -e "${GREEN}✅ Dependencies installed successfully.${NC}" echo -e "${GREEN}✅ Dependencies installed successfully.${NC}"
elif grep -qE "distutils installed project|uninstall-no-record-file|installed by debian" /tmp/pip_install.log; then
echo -e "${YELLOW}⚠️ Detected system package conflict, retrying with workaround...${NC}"
local IGNORE_PACKAGES=""
for pkg in PyYAML setuptools wheel certifi charset-normalizer; do
IGNORE_PACKAGES="$IGNORE_PACKAGES --ignore-installed $pkg"
done
set +e
$PYTHON_CMD -m pip install -r requirements.txt $IGNORE_PACKAGES $PIP_EXTRA_ARGS $PIP_MIRROR \
&& echo -e "${GREEN}✅ Dependencies installed successfully (workaround applied).${NC}" \
|| echo -e "${YELLOW}⚠️ Some dependencies may have issues, but continuing...${NC}"
set -e
elif grep -q "externally-managed-environment" /tmp/pip_install.log; then
echo -e "${YELLOW}⚠️ Detected externally-managed environment, retrying with --break-system-packages...${NC}"
set +e
$PYTHON_CMD -m pip install -r requirements.txt --break-system-packages $PIP_MIRROR \
&& echo -e "${GREEN}✅ Dependencies installed successfully (system packages override applied).${NC}" \
|| echo -e "${YELLOW}⚠️ Some dependencies may have issues, but continuing...${NC}"
set -e
else else
# Check if it's a distutils/system package conflict error echo -e "${YELLOW}⚠️ Installation had errors, but continuing...${NC}"
if grep -qE "distutils installed project|uninstall-no-record-file|installed by debian" /tmp/pip_install.log; then
echo -e "${YELLOW}⚠️ Detected system package conflict, retrying with workaround...${NC}"
# Only ignore common conflict packages
IGNORE_PACKAGES=""
for pkg in $COMMON_CONFLICT_PACKAGES; do
IGNORE_PACKAGES="$IGNORE_PACKAGES --ignore-installed $pkg"
done
if $PYTHON_CMD -m pip install -r requirements.txt $IGNORE_PACKAGES $PIP_EXTRA_ARGS -i https://pypi.tuna.tsinghua.edu.cn/simple; then
echo -e "${GREEN}✅ Dependencies installed successfully (workaround applied).${NC}"
else
echo -e "${YELLOW}⚠️ Some dependencies may have issues, but continuing...${NC}"
fi
elif grep -q "externally-managed-environment" /tmp/pip_install.log; then
echo -e "${YELLOW}⚠️ Detected externally-managed environment, retrying with --break-system-packages...${NC}"
if $PYTHON_CMD -m pip install -r requirements.txt --break-system-packages -i https://pypi.tuna.tsinghua.edu.cn/simple; then
echo -e "${GREEN}✅ Dependencies installed successfully (system packages override applied).${NC}"
else
echo -e "${YELLOW}⚠️ Some dependencies may have issues, but continuing...${NC}"
fi
else
echo -e "${YELLOW}⚠️ Installation had errors, but continuing...${NC}"
fi
fi fi
rm -f /tmp/pip_install.log rm -f /tmp/pip_install.log
} }
@@ -295,108 +275,48 @@ select_model() {
done done
} }
# Read model config: provider, default_model, key_variable_name
read_model_config() {
local provider=$1 default_model=$2 key_var=$3
echo -e "${GREEN}Configuring ${provider}...${NC}"
read -p "Enter ${provider} API Key: " _api_key
read -p "Enter model name [press Enter for default: ${default_model}]: " model_name
model_name=${model_name:-$default_model}
MODEL_NAME="$model_name"
eval "${key_var}=\"\$_api_key\""
}
# Read optional API base URL
read_api_base() {
local base_var=$1 default_url=$2
read -p "Enter API Base URL [press Enter for default: ${default_url}]: " api_base
api_base=${api_base:-$default_url}
eval "${base_var}=\"\$api_base\""
}
# Configure model # Configure model
configure_model() { configure_model() {
case "$model_choice" in case "$model_choice" in
1) 1) read_model_config "MiniMax" "MiniMax-M2.7" "MINIMAX_KEY" ;;
# MiniMax 2) read_model_config "Zhipu AI" "glm-5-turbo" "ZHIPU_KEY" ;;
echo -e "${GREEN}Configuring MiniMax...${NC}" 3) read_model_config "Kimi (Moonshot)" "kimi-k2.5" "MOONSHOT_KEY" ;;
read -p "Enter MiniMax API Key: " minimax_key 4) read_model_config "Doubao (Volcengine Ark)" "doubao-seed-2-0-code-preview-260215" "ARK_KEY" ;;
read -p "Enter model name [press Enter for default: MiniMax-M2.7]: " model_name 5) read_model_config "Qwen (DashScope)" "qwen3.5-plus" "DASHSCOPE_KEY" ;;
model_name=${model_name:-MiniMax-M2.7}
MODEL_NAME="$model_name"
MINIMAX_KEY="$minimax_key"
;;
2)
# Zhipu AI
echo -e "${GREEN}Configuring Zhipu AI...${NC}"
read -p "Enter Zhipu AI API Key: " zhipu_key
read -p "Enter model name [press Enter for default: glm-5-turbo]: " model_name
model_name=${model_name:-glm-5-turbo}
MODEL_NAME="$model_name"
ZHIPU_KEY="$zhipu_key"
;;
3)
# Kimi (Moonshot)
echo -e "${GREEN}Configuring Kimi (Moonshot)...${NC}"
read -p "Enter Moonshot API Key: " moonshot_key
read -p "Enter model name [press Enter for default: kimi-k2.5]: " model_name
model_name=${model_name:-kimi-k2.5}
MODEL_NAME="$model_name"
MOONSHOT_KEY="$moonshot_key"
;;
4)
# Doubao (Volcengine Ark)
echo -e "${GREEN}Configuring Doubao (Volcengine Ark)...${NC}"
read -p "Enter Ark API Key: " ark_key
read -p "Enter model name [press Enter for default: doubao-seed-2-0-code-preview-260215]: " model_name
model_name=${model_name:-doubao-seed-2-0-code-preview-260215}
MODEL_NAME="$model_name"
ARK_KEY="$ark_key"
;;
5)
# Qwen (DashScope)
echo -e "${GREEN}Configuring Qwen (DashScope)...${NC}"
read -p "Enter DashScope API Key: " dashscope_key
read -p "Enter model name [press Enter for default: qwen3.5-plus]: " model_name
model_name=${model_name:-qwen3.5-plus}
MODEL_NAME="$model_name"
DASHSCOPE_KEY="$dashscope_key"
;;
6) 6)
# Claude read_model_config "Claude" "claude-sonnet-4-6" "CLAUDE_KEY"
echo -e "${GREEN}Configuring Claude...${NC}" read_api_base "CLAUDE_BASE" "https://api.anthropic.com/v1"
read -p "Enter Claude API Key: " claude_key
read -p "Enter model name [press Enter for default: claude-sonnet-4-6]: " model_name
model_name=${model_name:-claude-sonnet-4-6}
read -p "Enter API Base URL [press Enter for default: https://api.anthropic.com/v1]: " api_base
api_base=${api_base:-https://api.anthropic.com/v1}
MODEL_NAME="$model_name"
CLAUDE_KEY="$claude_key"
CLAUDE_BASE="$api_base"
;; ;;
7) 7)
# Gemini read_model_config "Gemini" "gemini-3.1-pro-preview" "GEMINI_KEY"
echo -e "${GREEN}Configuring Gemini...${NC}" read_api_base "GEMINI_BASE" "https://generativelanguage.googleapis.com"
read -p "Enter Gemini API Key: " gemini_key
read -p "Enter model name [press Enter for default: gemini-3.1-pro-preview]: " model_name
model_name=${model_name:-gemini-3.1-pro-preview}
read -p "Enter API Base URL [press Enter for default: https://generativelanguage.googleapis.com]: " api_base
api_base=${api_base:-https://generativelanguage.googleapis.com}
MODEL_NAME="$model_name"
GEMINI_KEY="$gemini_key"
GEMINI_BASE="$api_base"
;; ;;
8) 8)
# OpenAI read_model_config "OpenAI GPT" "gpt-5.4" "OPENAI_KEY"
echo -e "${GREEN}Configuring OpenAI GPT...${NC}" read_api_base "OPENAI_BASE" "https://api.openai.com/v1"
read -p "Enter OpenAI API Key: " openai_key
read -p "Enter model name [press Enter for default: gpt-5.4]: " model_name
model_name=${model_name:-gpt-5.4}
read -p "Enter API Base URL [press Enter for default: https://api.openai.com/v1]: " api_base
api_base=${api_base:-https://api.openai.com/v1}
MODEL_NAME="$model_name"
OPENAI_KEY="$openai_key"
OPENAI_BASE="$api_base"
;; ;;
9) 9)
# LinkAI read_model_config "LinkAI" "MiniMax-M2.7" "LINKAI_KEY"
echo -e "${GREEN}Configuring LinkAI...${NC}"
read -p "Enter LinkAI API Key: " linkai_key
read -p "Enter model name [press Enter for default: MiniMax-M2.7]: " model_name
model_name=${model_name:-MiniMax-M2.7}
MODEL_NAME="$model_name"
USE_LINKAI="true" USE_LINKAI="true"
LINKAI_KEY="$linkai_key"
;; ;;
esac esac
} }
@@ -514,227 +434,86 @@ configure_channel() {
# Generate config file # Generate config file
create_config_file() { create_config_file() {
echo -e "${GREEN}📝 Generating config.json...${NC}" echo -e "${GREEN}📝 Generating config.json...${NC}"
# Build JSON based on channel type CHANNEL_TYPE="$CHANNEL_TYPE" \
case "$CHANNEL_TYPE" in MODEL_NAME="$MODEL_NAME" \
feishu) OPENAI_KEY="${OPENAI_KEY:-}" \
cat > config.json <<EOF OPENAI_BASE="${OPENAI_BASE:-https://api.openai.com/v1}" \
{ CLAUDE_KEY="${CLAUDE_KEY:-}" \
"channel_type": "feishu", CLAUDE_BASE="${CLAUDE_BASE:-https://api.anthropic.com/v1}" \
"model": "${MODEL_NAME}", GEMINI_KEY="${GEMINI_KEY:-}" \
"open_ai_api_key": "${OPENAI_KEY:-}", GEMINI_BASE="${GEMINI_BASE:-https://generativelanguage.googleapis.com}" \
"open_ai_api_base": "${OPENAI_BASE:-https://api.openai.com/v1}", ZHIPU_KEY="${ZHIPU_KEY:-}" \
"claude_api_key": "${CLAUDE_KEY:-}", MOONSHOT_KEY="${MOONSHOT_KEY:-}" \
"claude_api_base": "${CLAUDE_BASE:-https://api.anthropic.com/v1}", ARK_KEY="${ARK_KEY:-}" \
"gemini_api_key": "${GEMINI_KEY:-}", DASHSCOPE_KEY="${DASHSCOPE_KEY:-}" \
"gemini_api_base": "${GEMINI_BASE:-https://generativelanguage.googleapis.com}", MINIMAX_KEY="${MINIMAX_KEY:-}" \
"zhipu_ai_api_key": "${ZHIPU_KEY:-}", USE_LINKAI="${USE_LINKAI:-false}" \
"moonshot_api_key": "${MOONSHOT_KEY:-}", LINKAI_KEY="${LINKAI_KEY:-}" \
"ark_api_key": "${ARK_KEY:-}", FEISHU_BOT_NAME="${FEISHU_BOT_NAME:-}" \
"dashscope_api_key": "${DASHSCOPE_KEY:-}", FEISHU_APP_ID="${FEISHU_APP_ID:-}" \
"minimax_api_key": "${MINIMAX_KEY:-}", FEISHU_APP_SECRET="${FEISHU_APP_SECRET:-}" \
"voice_to_text": "openai", WEB_PORT="${WEB_PORT:-}" \
"text_to_voice": "openai", DT_CLIENT_ID="${DT_CLIENT_ID:-}" \
"voice_reply_voice": false, DT_CLIENT_SECRET="${DT_CLIENT_SECRET:-}" \
"speech_recognition": true, WECOM_BOT_ID="${WECOM_BOT_ID:-}" \
"group_speech_recognition": false, WECOM_BOT_SECRET="${WECOM_BOT_SECRET:-}" \
"use_linkai": ${USE_LINKAI:-false}, QQ_APP_ID="${QQ_APP_ID:-}" \
"linkai_api_key": "${LINKAI_KEY:-}", QQ_APP_SECRET="${QQ_APP_SECRET:-}" \
"linkai_app_code": "", WECHATCOM_CORP_ID="${WECHATCOM_CORP_ID:-}" \
"feishu_bot_name": "${FEISHU_BOT_NAME}", WECHATCOM_TOKEN="${WECHATCOM_TOKEN:-}" \
"feishu_app_id": "${FEISHU_APP_ID}", WECHATCOM_SECRET="${WECHATCOM_SECRET:-}" \
"feishu_app_secret": "${FEISHU_APP_SECRET}", WECHATCOM_AGENT_ID="${WECHATCOM_AGENT_ID:-}" \
"dingtalk_client_id": "", WECHATCOM_AES_KEY="${WECHATCOM_AES_KEY:-}" \
"dingtalk_client_secret": "", WECHATCOM_PORT="${WECHATCOM_PORT:-}" \
"agent": true, $PYTHON_CMD -c "
"agent_max_context_tokens": 40000, import json, os
"agent_max_context_turns": 30, e = os.environ.get
"agent_max_steps": 15 base = {
'channel_type': e('CHANNEL_TYPE'),
'model': e('MODEL_NAME'),
'open_ai_api_key': e('OPENAI_KEY', ''),
'open_ai_api_base': e('OPENAI_BASE'),
'claude_api_key': e('CLAUDE_KEY', ''),
'claude_api_base': e('CLAUDE_BASE'),
'gemini_api_key': e('GEMINI_KEY', ''),
'gemini_api_base': e('GEMINI_BASE'),
'zhipu_ai_api_key': e('ZHIPU_KEY', ''),
'moonshot_api_key': e('MOONSHOT_KEY', ''),
'ark_api_key': e('ARK_KEY', ''),
'dashscope_api_key': e('DASHSCOPE_KEY', ''),
'minimax_api_key': e('MINIMAX_KEY', ''),
'voice_to_text': 'openai',
'text_to_voice': 'openai',
'voice_reply_voice': False,
'speech_recognition': True,
'group_speech_recognition': False,
'use_linkai': e('USE_LINKAI') == 'true',
'linkai_api_key': e('LINKAI_KEY', ''),
'linkai_app_code': '',
'agent': True,
'agent_max_context_tokens': 40000,
'agent_max_context_turns': 30,
'agent_max_steps': 15,
} }
EOF channel_map = {
;; 'feishu': {'feishu_bot_name': 'FEISHU_BOT_NAME', 'feishu_app_id': 'FEISHU_APP_ID', 'feishu_app_secret': 'FEISHU_APP_SECRET'},
web) 'web': {'web_port': ('WEB_PORT', int)},
cat > config.json <<EOF 'dingtalk': {'dingtalk_client_id': 'DT_CLIENT_ID', 'dingtalk_client_secret': 'DT_CLIENT_SECRET'},
{ 'wecom_bot': {'wecom_bot_id': 'WECOM_BOT_ID', 'wecom_bot_secret': 'WECOM_BOT_SECRET'},
"channel_type": "web", 'qq': {'qq_app_id': 'QQ_APP_ID', 'qq_app_secret': 'QQ_APP_SECRET'},
"web_port": ${WEB_PORT}, 'wechatcom_app': {'wechatcom_corp_id': 'WECHATCOM_CORP_ID', 'wechatcomapp_token': 'WECHATCOM_TOKEN', 'wechatcomapp_secret': 'WECHATCOM_SECRET', 'wechatcomapp_agent_id': 'WECHATCOM_AGENT_ID', 'wechatcomapp_aes_key': 'WECHATCOM_AES_KEY', 'wechatcomapp_port': ('WECHATCOM_PORT', int)},
"model": "${MODEL_NAME}",
"open_ai_api_key": "${OPENAI_KEY:-}",
"open_ai_api_base": "${OPENAI_BASE:-https://api.openai.com/v1}",
"claude_api_key": "${CLAUDE_KEY:-}",
"claude_api_base": "${CLAUDE_BASE:-https://api.anthropic.com/v1}",
"gemini_api_key": "${GEMINI_KEY:-}",
"gemini_api_base": "${GEMINI_BASE:-https://generativelanguage.googleapis.com}",
"zhipu_ai_api_key": "${ZHIPU_KEY:-}",
"moonshot_api_key": "${MOONSHOT_KEY:-}",
"ark_api_key": "${ARK_KEY:-}",
"dashscope_api_key": "${DASHSCOPE_KEY:-}",
"minimax_api_key": "${MINIMAX_KEY:-}",
"voice_to_text": "openai",
"text_to_voice": "openai",
"voice_reply_voice": false,
"speech_recognition": true,
"group_speech_recognition": false,
"use_linkai": ${USE_LINKAI:-false},
"linkai_api_key": "${LINKAI_KEY:-}",
"linkai_app_code": "",
"feishu_bot_name": "$feishu_bot_name",
"feishu_app_id": "",
"feishu_app_secret": "",
"dingtalk_client_id": "",
"dingtalk_client_secret": "",
"agent": true,
"agent_max_context_tokens": 40000,
"agent_max_context_turns": 30,
"agent_max_steps": 15
} }
EOF ch = e('CHANNEL_TYPE')
;; for key, spec in channel_map.get(ch, {}).items():
dingtalk) if isinstance(spec, tuple):
cat > config.json <<EOF env_name, conv = spec
{ base[key] = conv(e(env_name))
"channel_type": "dingtalk", else:
"model": "${MODEL_NAME}", base[key] = e(spec, '')
"open_ai_api_key": "${OPENAI_KEY:-}", with open('config.json', 'w') as f:
"open_ai_api_base": "${OPENAI_BASE:-https://api.openai.com/v1}", json.dump(base, f, indent=2, ensure_ascii=False)
"claude_api_key": "${CLAUDE_KEY:-}", "
"claude_api_base": "${CLAUDE_BASE:-https://api.anthropic.com/v1}",
"gemini_api_key": "${GEMINI_KEY:-}",
"gemini_api_base": "${GEMINI_BASE:-https://generativelanguage.googleapis.com}",
"zhipu_ai_api_key": "${ZHIPU_KEY:-}",
"moonshot_api_key": "${MOONSHOT_KEY:-}",
"ark_api_key": "${ARK_KEY:-}",
"dashscope_api_key": "${DASHSCOPE_KEY:-}",
"minimax_api_key": "${MINIMAX_KEY:-}",
"voice_to_text": "openai",
"text_to_voice": "openai",
"voice_reply_voice": false,
"speech_recognition": true,
"group_speech_recognition": false,
"use_linkai": ${USE_LINKAI:-false},
"linkai_api_key": "${LINKAI_KEY:-}",
"linkai_app_code": "",
"feishu_bot_name": "$feishu_bot_name",
"feishu_app_id": "",
"feishu_app_secret": "",
"dingtalk_client_id": "${DT_CLIENT_ID}",
"dingtalk_client_secret": "${DT_CLIENT_SECRET}",
"agent": true,
"agent_max_context_tokens": 40000,
"agent_max_context_turns": 30,
"agent_max_steps": 15
}
EOF
;;
wecom_bot)
cat > config.json <<EOF
{
"channel_type": "wecom_bot",
"wecom_bot_id": "${WECOM_BOT_ID}",
"wecom_bot_secret": "${WECOM_BOT_SECRET}",
"model": "${MODEL_NAME}",
"open_ai_api_key": "${OPENAI_KEY:-}",
"open_ai_api_base": "${OPENAI_BASE:-https://api.openai.com/v1}",
"claude_api_key": "${CLAUDE_KEY:-}",
"claude_api_base": "${CLAUDE_BASE:-https://api.anthropic.com/v1}",
"gemini_api_key": "${GEMINI_KEY:-}",
"gemini_api_base": "${GEMINI_BASE:-https://generativelanguage.googleapis.com}",
"zhipu_ai_api_key": "${ZHIPU_KEY:-}",
"moonshot_api_key": "${MOONSHOT_KEY:-}",
"ark_api_key": "${ARK_KEY:-}",
"dashscope_api_key": "${DASHSCOPE_KEY:-}",
"minimax_api_key": "${MINIMAX_KEY:-}",
"voice_to_text": "openai",
"text_to_voice": "openai",
"voice_reply_voice": false,
"speech_recognition": true,
"group_speech_recognition": false,
"use_linkai": ${USE_LINKAI:-false},
"linkai_api_key": "${LINKAI_KEY:-}",
"linkai_app_code": "",
"agent": true,
"agent_max_context_tokens": 40000,
"agent_max_context_turns": 30,
"agent_max_steps": 15
}
EOF
;;
qq)
cat > config.json <<EOF
{
"channel_type": "qq",
"qq_app_id": "${QQ_APP_ID}",
"qq_app_secret": "${QQ_APP_SECRET}",
"model": "${MODEL_NAME}",
"open_ai_api_key": "${OPENAI_KEY:-}",
"open_ai_api_base": "${OPENAI_BASE:-https://api.openai.com/v1}",
"claude_api_key": "${CLAUDE_KEY:-}",
"claude_api_base": "${CLAUDE_BASE:-https://api.anthropic.com/v1}",
"gemini_api_key": "${GEMINI_KEY:-}",
"gemini_api_base": "${GEMINI_BASE:-https://generativelanguage.googleapis.com}",
"zhipu_ai_api_key": "${ZHIPU_KEY:-}",
"moonshot_api_key": "${MOONSHOT_KEY:-}",
"ark_api_key": "${ARK_KEY:-}",
"dashscope_api_key": "${DASHSCOPE_KEY:-}",
"minimax_api_key": "${MINIMAX_KEY:-}",
"voice_to_text": "openai",
"text_to_voice": "openai",
"voice_reply_voice": false,
"speech_recognition": true,
"group_speech_recognition": false,
"use_linkai": ${USE_LINKAI:-false},
"linkai_api_key": "${LINKAI_KEY:-}",
"linkai_app_code": "",
"agent": true,
"agent_max_context_tokens": 40000,
"agent_max_context_turns": 30,
"agent_max_steps": 15
}
EOF
;;
wechatcom_app)
cat > config.json <<EOF
{
"channel_type": "wechatcom_app",
"wechatcom_corp_id": "${WECHATCOM_CORP_ID}",
"wechatcomapp_token": "${WECHATCOM_TOKEN}",
"wechatcomapp_secret": "${WECHATCOM_SECRET}",
"wechatcomapp_agent_id": "${WECHATCOM_AGENT_ID}",
"wechatcomapp_aes_key": "${WECHATCOM_AES_KEY}",
"wechatcomapp_port": ${WECHATCOM_PORT},
"model": "${MODEL_NAME}",
"open_ai_api_key": "${OPENAI_KEY:-}",
"open_ai_api_base": "${OPENAI_BASE:-https://api.openai.com/v1}",
"claude_api_key": "${CLAUDE_KEY:-}",
"claude_api_base": "${CLAUDE_BASE:-https://api.anthropic.com/v1}",
"gemini_api_key": "${GEMINI_KEY:-}",
"gemini_api_base": "${GEMINI_BASE:-https://generativelanguage.googleapis.com}",
"zhipu_ai_api_key": "${ZHIPU_KEY:-}",
"moonshot_api_key": "${MOONSHOT_KEY:-}",
"ark_api_key": "${ARK_KEY:-}",
"dashscope_api_key": "${DASHSCOPE_KEY:-}",
"minimax_api_key": "${MINIMAX_KEY:-}",
"voice_to_text": "openai",
"text_to_voice": "openai",
"voice_reply_voice": false,
"speech_recognition": true,
"group_speech_recognition": false,
"use_linkai": ${USE_LINKAI:-false},
"linkai_api_key": "${LINKAI_KEY:-}",
"linkai_app_code": "",
"feishu_bot_name": "$feishu_bot_name",
"feishu_app_id": "",
"feishu_app_secret": "",
"dingtalk_client_id": "",
"dingtalk_client_secret": "",
"agent": true,
"agent_max_context_tokens": 40000,
"agent_max_context_turns": 30,
"agent_max_steps": 15
}
EOF
;;
esac
echo -e "${GREEN}✅ Configuration file created successfully.${NC}" echo -e "${GREEN}✅ Configuration file created successfully.${NC}"
} }
@@ -811,23 +590,24 @@ show_usage() {
echo -e "${CYAN}${BOLD}=========================================${NC}" echo -e "${CYAN}${BOLD}=========================================${NC}"
} }
# Check if service is running # Ensure PYTHON_CMD is set
is_running() { ensure_python_cmd() {
if [ -z "$PYTHON_CMD" ]; then if [ -z "$PYTHON_CMD" ]; then
detect_python_command 2>/dev/null || PYTHON_CMD="python3" detect_python_command 2>/dev/null || PYTHON_CMD="python3"
fi fi
pid=$(ps ax | grep -i app.py | grep "${BASE_DIR}" | grep "$PYTHON_CMD" | grep -v grep | awk '{print $1}')
[ -n "$pid" ]
} }
# Get service PID # Get service PID (empty string if not running)
get_pid() { get_pid() {
if [ -z "$PYTHON_CMD" ]; then ensure_python_cmd
detect_python_command 2>/dev/null || PYTHON_CMD="python3"
fi
ps ax | grep -i app.py | grep "${BASE_DIR}" | grep "$PYTHON_CMD" | grep -v grep | awk '{print $1}' ps ax | grep -i app.py | grep "${BASE_DIR}" | grep "$PYTHON_CMD" | grep -v grep | awk '{print $1}'
} }
# Check if service is running
is_running() {
[ -n "$(get_pid)" ]
}
# Start service # Start service
cmd_start() { cmd_start() {
# Check if config.json exists # Check if config.json exists
@@ -1023,67 +803,38 @@ install_mode() {
fi fi
} }
# Require running inside the project directory
require_project_dir() {
if [ "$IS_PROJECT_DIR" = false ]; then
echo -e "${RED}${EMOJI_CROSS} Must run in project directory${NC}"
exit 1
fi
}
# Main function # Main function
main() { main() {
case "$1" in case "$1" in
start) start|stop|restart|status|logs|config|update)
if [ "$IS_PROJECT_DIR" = false ]; then require_project_dir
echo -e "${RED}❌ Must run in project directory${NC}"
exit 1
fi
cmd_start
;;
stop)
if [ "$IS_PROJECT_DIR" = false ]; then
echo -e "${RED}❌ Must run in project directory${NC}"
exit 1
fi
cmd_stop
;;
restart)
if [ "$IS_PROJECT_DIR" = false ]; then
echo -e "${RED}❌ Must run in project directory${NC}"
exit 1
fi
cmd_restart
;;
status)
if [ "$IS_PROJECT_DIR" = false ]; then
echo -e "${RED}❌ Must run in project directory${NC}"
exit 1
fi
cmd_status
;;
logs)
if [ "$IS_PROJECT_DIR" = false ]; then
echo -e "${RED}❌ Must run in project directory${NC}"
exit 1
fi
cmd_logs
;;
config)
if [ "$IS_PROJECT_DIR" = false ]; then
echo -e "${RED}❌ Must run in project directory${NC}"
exit 1
fi
cmd_config
;;
update)
if [ "$IS_PROJECT_DIR" = false ]; then
echo -e "${RED}❌ Must run in project directory${NC}"
exit 1
fi
cmd_update
;; ;;
esac
case "$1" in
start) cmd_start ;;
stop) cmd_stop ;;
restart) cmd_restart ;;
status) cmd_status ;;
logs) cmd_logs ;;
config) cmd_config ;;
update) cmd_update ;;
help|--help|-h) help|--help|-h)
show_usage show_usage
;; ;;
"") "")
# No command - install/configure mode
install_mode install_mode
;; ;;
*) *)
echo -e "${RED} Unknown command: $1${NC}" echo -e "${RED}${EMOJI_CROSS} Unknown command: $1${NC}"
echo "" echo ""
show_usage show_usage
exit 1 exit 1