@@ -5,26 +5,27 @@ export PATH=$PATH:/usr/bin:/usr/local/bin
55
66RED=' \033[0;31m' ; GREEN=' \033[0;32m' ; YELLOW=' \033[1;33m' ; NC=' \033[0m'
77
8+ # 状态变量
89CLEANING=0
910PMHQ_PID=" "
1011USE_XVFB=0
1112DISTRO=" "
13+ PMHQ_BIN=" $SCRIPT_DIR /llbot/pmhq"
1214
1315log () { echo -e " ${GREEN} >>> $1 ${NC} " ; }
1416warn () { echo -e " ${YELLOW} >>> $1 ${NC} " ; }
1517error () { echo -e " ${RED} 错误: $1 ${NC} " ; exit 1; }
1618
1719check_sudo () {
1820 log " 验证 Sudo 权限..."
19- if ! sudo -v; then
20- error " Sudo 验证失败或被取消,脚本终止。"
21- fi
21+ sudo -v || error " Sudo 验证失败或被取消,脚本终止。"
2222}
2323
2424cleanup () {
2525 # 防止用户对 Ctrl+C 突然产生某种异样的迷恋然后狂按 Ctrl+C
2626 [ " $CLEANING " -eq 1 ] && return
2727 CLEANING=1
28+ trap ' ' SIGINT SIGTERM # 屏蔽后续 Ctrl+C,防止崩溃
2829
2930 # 防止日志挡交互
3031 if [ -n " $PMHQ_PID " ] && kill -0 " $PMHQ_PID " 2> /dev/null; then
@@ -38,9 +39,17 @@ cleanup() {
3839 local choice=" "
3940
4041 if [ -n " $PMHQ_PID " ]; then
41- if read -t 5 -n 1 -r -p " 是否关闭 QQ 及相关进程? [Y/n] (5秒后默认关闭): " choice < /dev/tty; then
42- echo " "
43- else
42+ local T_COUNT=5
43+ while [ $T_COUNT -gt 0 ]; do
44+ printf " \r是否关闭 PMHQ 及 QQ 相关进程? [Y/n] (${T_COUNT} 秒后默认关闭): "
45+ if read -t 1 -n 1 choice < /dev/tty; then
46+ echo " "
47+ break
48+ fi
49+ (( T_COUNT-- ))
50+ done
51+
52+ if [ $T_COUNT -eq 0 ]; then
4453 echo " "
4554 log " 等待超时,执行默认关闭操作。"
4655 fi
@@ -52,25 +61,21 @@ cleanup() {
5261
5362 if [ $kill_qq -eq 1 ]; then
5463 warn " 正在停止服务..."
55-
56- kill -CONT -" $PMHQ_PID " 2> /dev/null
57-
58- kill -TERM -" $PMHQ_PID " 2> /dev/null
64+ [ -n " $PMHQ_PID " ] && kill -CONT -" $PMHQ_PID " 2> /dev/null
65+ [ -n " $PMHQ_PID " ] && kill -TERM -" $PMHQ_PID " 2> /dev/null
5966 pkill -15 -f " $PMHQ_BIN " 2> /dev/null
6067 pkill -15 -f " /opt/QQ/qq" 2> /dev/null
6168
6269 local wait_count=0
63-
64- while kill -0 " $PMHQ_PID " 2> /dev/null || \
65- pgrep -f " $PMHQ_BIN " > /dev/null || \
66- pgrep -f " /opt/QQ/qq" > /dev/null; do
70+ while kill -0 " $PMHQ_PID " 2> /dev/null || pgrep -f " $PMHQ_BIN " > /dev/null || pgrep -f " /opt/QQ/qq" > /dev/null; do
6771 sleep 0.5
6872 (( wait_count++ ))
6973 if [ $wait_count -ge 6 ]; then
7074 warn " 检测到残留进程,执行强制清理..."
7175 # 气死我了,总有点关不掉进程的毛病,累了,跟我 pkill -9 说去吧
76+ # 可能会误伤别的 QQ 的进程罢
7277 # 期待大手子修复
73- kill -KILL -" $PMHQ_PID " 2> /dev/null
78+ [ -n " $PMHQ_PID " ] && kill -9 -" $PMHQ_PID " 2> /dev/null
7479 pkill -9 -f " $PMHQ_BIN " 2> /dev/null
7580 pkill -9 -f " /opt/QQ/qq" 2> /dev/null
7681 break
@@ -79,16 +84,13 @@ cleanup() {
7984
8085 # 环境清理
8186 if [ " $DISTRO " != " arch" ] && [ " $USE_XVFB " -eq 0 ]; then
82- if command -v xhost & > /dev/null; then
83- xhost -local:$( whoami) > /dev/null 2>&1
84- fi
87+ command -v xhost & > /dev/null && xhost -local:$( whoami) > /dev/null 2>&1
8588 fi
8689 log " 所有进程已清理完毕。"
8790 else
8891 # 选择不关闭时必须恢复进程运行
89- kill -CONT -" $PMHQ_PID " 2> /dev/null
92+ [ -n " $PMHQ_PID " ] && kill -CONT -" $PMHQ_PID " 2> /dev/null
9093 log " 已恢复后台进程运行 (PGID: $PMHQ_PID )"
91- warn " 注意:它们已脱离脚本控制,后续请手动管理"
9294 fi
9395
9496 exit 0
@@ -97,7 +99,8 @@ cleanup() {
9799trap cleanup SIGINT SIGTERM
98100
99101confirm () {
100- read -n 1 -s -r -p " $1 (Y/n) " key
102+ local key=" "
103+ read -n 1 -s -r -p " $1 (Y/n) " key < /dev/tty
101104 echo " "
102105 [[ " $key " == " Y" || " $key " == " y" || " $key " == " " ]]
103106}
@@ -110,17 +113,18 @@ find_port() {
110113 return 0
111114 fi
112115 # 回退方案:扫描可用端口
113- local port =$1
114- while [ $port -lt 65535 ]; do
115- if ! ss -tuln 2> /dev/null | grep -q " :$port " && ! netstat -tuln 2> /dev/null | grep -q " :$port " ; then
116- echo $port
116+ local p =$1
117+ while [ $p -lt 65535 ]; do
118+ if ! ss -tuln 2> /dev/null | grep -q " :$p " && ! netstat -tuln 2> /dev/null | grep -q " :$p " ; then
119+ echo $p
117120 return 0
118121 fi
119- (( port ++ ))
122+ (( p ++ ))
120123 done
121124 return 1
122125}
123126
127+ # 环境检查
124128if command -v pacman & > /dev/null; then
125129 DISTRO=" arch"
126130elif command -v apt & > /dev/null; then
@@ -138,12 +142,12 @@ install_arch() {
138142 if [ ! -f " /opt/QQ/qq" ] && confirm " 未检测到 QQ,是否通过 AUR 安装?" ; then
139143 if ! command -v yay & > /dev/null; then
140144 warn " 未检测到 yay,尝试安装..."
141- if ! sudo pacman -S --needed --noconfirm yay 2> /dev/null ; then
142- warn " pacman 安装 yay 失败,切换源码编译... "
143- rm -rf /tmp/yay_install && git clone https://aur.archlinux.org/yay.git /tmp/yay_install
144- (cd /tmp/yay_install && makepkg -si --noconfirm) || error " yay 编译失败"
145- rm -rf /tmp/yay_install
146- fi
145+ sudo pacman -S --needed --noconfirm yay || {
146+ local TMP_DIR= " /tmp/yay_install "
147+ rm -rf " $TMP_DIR " && git clone https://aur.archlinux.org/yay.git " $TMP_DIR "
148+ (cd " $TMP_DIR " && makepkg -si --noconfirm) || { rm -rf " $TMP_DIR " ; error " yay 编译失败" ; }
149+ rm -rf " $TMP_DIR "
150+ }
147151 fi
148152 yay -S --noconfirm linuxqq || error " LinuxQQ 安装失败"
149153 fi
@@ -152,53 +156,29 @@ install_arch() {
152156install_debian () {
153157 check_sudo
154158 local MACHINE=$( uname -m)
155- local ARCH=" "
156159 case " $MACHINE " in
157- x86_64) ARCH=" amd64" ;;
158- aarch64) ARCH=" arm64" ;;
159- * ) error " 不支持的架构: $MACHINE " ;;
160+ x86_64) ARCH=" amd64" ;;
161+ aarch64) ARCH=" arm64" ;;
162+ * ) error " 不支持的架构: $MACHINE " ;;
160163 esac
161164
162165 if [ ! -f " /opt/QQ/qq" ] && confirm " 未检测到 QQ,是否安装?" ; then
163- log " 下载并安装 QQ ($ARCH )..."
164- sudo apt-get update && sudo apt-get install -y wget || error " apt update 或 wget 安装失败"
165-
166+ sudo apt-get update && sudo apt-get install -y wget || error " 基础工具安装失败"
166167 local DEB=" /tmp/qq.deb"
167- wget -O " $DEB " " https://dldir1v6.qq.com/qqfile/qq/QQNT/ab90fdfa/linuxqq_3.2.20-40768_$ARCH .deb" || error " QQ 安装包下载失败"
168-
168+ wget -O " $DEB " " https://dldir1v6.qq.com/qqfile/qq/QQNT/ab90fdfa/linuxqq_3.2.20-40768_$ARCH .deb" || error " 下载失败"
169169 local LIB_SND=" libasound2"
170- if apt-cache show libasound2t64 & > /dev/null; then
171- LIB_SND=" libasound2t64"
172- fi
173- log " 使用 ALSA 库包名: $LIB_SND "
174-
175- sudo apt install -y " $DEB " x11-utils libgtk-3-0 libxcb-xinerama0 libgl1-mesa-dri libnotify4 libnss3 xdg-utils libsecret-1-0 libappindicator3-1 libgbm1 $LIB_SND fonts-noto-cjk libxss1 || error " QQ 依赖安装失败"
170+ apt-cache show libasound2t64 & > /dev/null && LIB_SND=" libasound2t64"
171+ sudo apt install -y " $DEB " x11-utils libgtk-3-0 libxcb-xinerama0 libgl1-mesa-dri libnotify4 libnss3 xdg-utils libsecret-1-0 libappindicator3-1 libgbm1 $LIB_SND fonts-noto-cjk libxss1 || error " 依赖安装失败"
176172 rm -f " $DEB "
177173 fi
178-
179- # 检查其他工具
180- local missing_pkgs=" "
181- for pkg in ffmpeg xvfb; do
182- if ! command -v $pkg & > /dev/null; then
183- missing_pkgs=" $missing_pkgs $pkg "
184- fi
185- done
186-
187- if [ -n " $missing_pkgs " ]; then
188- log " 安装缺失工具: $missing_pkgs "
189- sudo apt-get install -y $missing_pkgs || error " 工具安装失败"
190- fi
174+ sudo apt-get install -y ffmpeg xvfb || error " 工具安装失败"
191175}
192176
193177# 执行安装
194- if [ " $DISTRO " == " arch" ]; then
195- install_arch
196- else
197- install_debian
198- fi
199-
200- chmod +x " $SCRIPT_DIR /llbot/node" " $SCRIPT_DIR /llbot/pmhq" 2> /dev/null
178+ [ " $DISTRO " == " arch" ] && install_arch || install_debian
201179
180+ # 配置权限
181+ chmod +x " $SCRIPT_DIR /llbot/node" " $PMHQ_BIN " 2> /dev/null
202182sudo chown -R $( whoami) :$( whoami) " $SCRIPT_DIR /llbot" 2> /dev/null
203183
204184PORT=$( find_port 13000)
@@ -207,75 +187,61 @@ log "分配端口: $PORT"
207187
208188echo " ------------------------------------------------"
209189echo " 1) GUI 模式 (有界面)"
210- echo " 2) Shell 模式 (无界面/Headless,默认 )"
190+ echo " 2) Shell 模式 (无界面)"
211191echo " ------------------------------------------------"
212192
213193MODE_CHOICE=" "
214194TIMEOUT=5
215195while [ $TIMEOUT -gt 0 ]; do
216196 printf " \r请选择 [1/2] (${TIMEOUT} 秒后自动选择 Shell): "
217- if read -t 1 -n 1 MODE_CHOICE; then
197+ # 使用 tty 确保 read 不被管道干扰
198+ if read -t 1 -n 1 MODE_CHOICE < /dev/tty; then
218199 echo " "
219200 break
220201 fi
221202 (( TIMEOUT-- ))
222203done
223- [ $TIMEOUT -eq 0 ] && echo " " && log " 超时,自动选择 Shell 模式"
204+ [ -z " $MODE_CHOICE " ] && { echo " " ; log " 超时,自动选择 Shell 模式" ; }
224205
225206MODE_CHOICE=${MODE_CHOICE:- 2}
226207USE_XVFB=$( [ " $MODE_CHOICE " == " 2" ] && echo 1 || echo 0)
227208
209+ # X11/Wayland 变量处理
228210if [ $USE_XVFB -eq 0 ]; then
229211 if command -v xauth & > /dev/null; then
230212 export XAUTHORITY=${XAUTHORITY:- $HOME / .Xauthority}
231213 else
232- warn " 未检测到 xauth,使用临时 xhost 授权"
233214 xhost +local:$( whoami) > /dev/null 2>&1
234215 fi
235216fi
236217
237- IM_ENV=" "
218+ IM_ENV=" XMODIFIERS=@im=fcitx "
238219EXTRA_FLAGS=" "
239-
240220if [[ " $XDG_SESSION_TYPE " == " wayland" || -n " $WAYLAND_DISPLAY " ]]; then
241- log " 检测到 Wayland 环境"
242- IM_ENV=" XMODIFIERS=@im=fcitx"
243221 EXTRA_FLAGS=" --enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime"
244222else
245- log " 检测到 X11 环境"
246- IM_ENV=" GTK_IM_MODULE=fcitx QT_IM_MODULE=fcitx XMODIFIERS=@im=fcitx SDL_IM_MODULE=fcitx GLFW_IM_MODULE=ibus"
223+ IM_ENV=" GTK_IM_MODULE=fcitx QT_IM_MODULE=fcitx $IM_ENV SDL_IM_MODULE=fcitx GLFW_IM_MODULE=ibus"
247224fi
248225
249- NODE_BIN=" $SCRIPT_DIR /llbot/node"
250- LLBOT_JS=" $SCRIPT_DIR /llbot/llbot.js"
251- PMHQ_BIN=" $SCRIPT_DIR /llbot/pmhq"
252-
253226run_llbot () {
254227 set -m
255-
256228 if [ " $DISTRO " == " arch" ]; then
257229 export LD_PRELOAD=" /usr/lib/libstdc++.so.6:/usr/lib/libgcc_s.so.1"
258230 export DBUS_SESSION_BUS_ADDRESS=" unix:path=/run/user/$( id -u) /bus"
259231 fi
260232
261- local sub_cmd=" $NODE_BIN --enable-source-maps $LLBOT_JS -- --pmhq-port=$PORT --no-sandbox $EXTRA_FLAGS "
262-
263- log " 正在启动 LLBot... (模式: $( [ $USE_XVFB -eq 1 ] && echo " Headless" || echo " GUI" ) )"
264- log " 按 Ctrl+C 可停止运行"
233+ local sub_cmd=" $SCRIPT_DIR /llbot/node --enable-source-maps $SCRIPT_DIR /llbot/llbot.js -- --pmhq-port=$PORT --no-sandbox $EXTRA_FLAGS "
234+ log " 启动模式: $( [ $USE_XVFB -eq 1 ] && echo " Headless" || echo " GUI" ) "
265235
266236 if [ $USE_XVFB -eq 1 ]; then
267237 env $IM_ENV xvfb-run -a " $PMHQ_BIN " --port=" $PORT " --sub-cmd=" $sub_cmd " &
268238 else
269- if [ " $DISTRO " != " arch" ]; then
270- xhost +local:$( whoami) > /dev/null 2>&1
271- fi
239+ [ " $DISTRO " != " arch" ] && xhost +local:$( whoami) > /dev/null 2>&1
272240 env $IM_ENV " $PMHQ_BIN " --port=" $PORT " --sub-cmd=" $sub_cmd " &
273241 fi
274242
275243 PMHQ_PID=$!
276-
277- # 阻塞等待进程结束
278- wait " $PMHQ_PID " || true
244+ wait " $PMHQ_PID " 2> /dev/null
279245}
280246
281247run_llbot
0 commit comments