mirror of
https://github.com/nickrunning/wechat-selkies.git
synced 2026-05-09 00:24:09 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ecc68281c3 | ||
|
|
6d039cf06e | ||
|
|
e18821f917 | ||
|
|
c7fd1ec6d2 |
94
AGENTS.md
Normal file
94
AGENTS.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# PROJECT KNOWLEDGE BASE
|
||||
|
||||
**Generated:** 2026-03-23
|
||||
**Commit:** e18821f
|
||||
**Branch:** master
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
Docker 容器化微信/QQ Linux 客户端,基于 LinuxServer Selkies WebRTC 基础镜像,通过浏览器远程访问桌面应用。支持 AMD64/ARM64 双架构。
|
||||
|
||||
## STRUCTURE
|
||||
|
||||
```
|
||||
wechat-selkies/
|
||||
├── Dockerfile # 单阶段构建,基于 selkies:ubuntunoble
|
||||
├── docker-compose.yml # 单服务编排
|
||||
├── root/ # COPY /root / → 容器内脚本和配置
|
||||
│ ├── defaults/
|
||||
│ │ ├── autostart # 容器启动入口,仅一行: /scripts/start.sh
|
||||
│ │ └── menu.xml # Openbox 右键菜单模板(静态基础项)
|
||||
│ └── scripts/
|
||||
│ ├── start.sh # 主启动脚本:openbox配置 → 菜单刷新 → 监听桌面 → 启动应用
|
||||
│ ├── refresh-menu.sh # 合并 menu.xml + ~/Desktop/*.desktop → 生成最终菜单
|
||||
│ ├── window_switcher.py # ⚠️ 已废弃,仅保留
|
||||
│ ├── wechat/ # wechat-start.sh / wechat-restart.sh / wechat-unminimize.sh
|
||||
│ └── qq/ # qq-restart.sh
|
||||
├── .github/workflows/ # CI: 多架构构建 + Issue 自动化
|
||||
├── docs/images/ # README 截图
|
||||
└── config/ # ⚠️ gitignored 运行时数据挂载点
|
||||
```
|
||||
|
||||
## WHERE TO LOOK
|
||||
|
||||
| 任务 | 位置 | 备注 |
|
||||
|------|------|------|
|
||||
| 添加新应用 | `Dockerfile` (安装) + `root/defaults/menu.xml` (菜单) | 参考 QQ 安装段落的 case 模式 |
|
||||
| 修改启动行为 | `root/scripts/start.sh` | 入口脚本,由 `root/defaults/autostart` 触发 |
|
||||
| 修改右键菜单 | `root/defaults/menu.xml` + `root/scripts/refresh-menu.sh` | menu.xml 是模板,refresh-menu.sh 动态合并 ~/Desktop/*.desktop |
|
||||
| 修改菜单动态生成逻辑 | `root/scripts/refresh-menu.sh` | 解析 .desktop 文件的 Name/Exec/Icon 字段,处理图标搜索 |
|
||||
| 添加新应用启动/重启脚本 | `root/scripts/<app>/` | 遵循 wechat/ 目录模式: start.sh + restart.sh |
|
||||
| 修改环境变量 | `Dockerfile` (ENV) + `docker-compose.yml` (environment) | 两处需同步 |
|
||||
| CI/CD | `.github/workflows/docker.yml` | tag v* 触发,多架构 buildx,可选推送 Docker Hub |
|
||||
| Issue 自动化 | `.github/workflows/issue-validation.yml` + `cleanup-issues.yml` | 标题/描述校验 + 7天自动关闭 |
|
||||
|
||||
## CONVENTIONS
|
||||
|
||||
- **语言**: 脚本用 Bash (#!/bin/bash),窗口切换器用 Python3 + Xlib + tkinter
|
||||
- **容器路径约定**: `COPY /root /` 将 `root/` 目录内容映射到容器根 `/`,因此 `root/scripts/start.sh` → 容器内 `/scripts/start.sh`
|
||||
- **自启动机制**: Selkies 基础镜像读取 `/defaults/autostart` 文件执行启动脚本
|
||||
- **菜单刷新**: inotifywait 监听 `~/Desktop/` 变更自动刷新 Openbox 菜单
|
||||
- **进程管理**: 应用通过 `nohup ... &` 后台启动,restart 脚本先 `pkill -9` 再启动
|
||||
- **restart 脚本模式**: `pkill -9 -f /usr/bin/<app>` → `nohup /usr/bin/<app> >/dev/null 2>&1 &`(QQ 需额外 `--no-sandbox`)
|
||||
- **菜单模板 vs 运行时**: `root/defaults/menu.xml` 是源码模板,`/config/.config/openbox/menu.xml` 是运行时生成的,不要混淆
|
||||
- **refresh-menu.sh**: 仅在内容变更时才写入 + reconfigure,避免无意义刷新
|
||||
- **图标搜索优先级**: proot-apps icons (256→512→128→64→48→scalable) → system icons → fallback xterm icon
|
||||
- **多架构**: Dockerfile 使用 `$TARGETPLATFORM` 条件分支安装不同架构 deb 包
|
||||
|
||||
## ANTI-PATTERNS (THIS PROJECT)
|
||||
|
||||
- **不要直接修改 `config/` 目录下的文件** — 这是运行时挂载数据,gitignored
|
||||
- **不要修改 `config/.config/openbox/` 下文件作为"源码"** — 这些由 `root/defaults/` 模板生成
|
||||
- **不要在 `root/` 下放非容器必需文件** — `COPY /root /` 会将所有内容复制到容器根目录
|
||||
- **不要在 menu.xml 中添加 .desktop 动态条目** — 由 refresh-menu.sh 自动处理
|
||||
- **不要在 start.sh 中用 `exec` 替代 `nohup ... &`** — 需要多进程并行运行
|
||||
- **升级后功能缺失** → 清空 `./config/.config/openbox/` 目录(已知 gotcha,见 README)
|
||||
- **window_switcher.py 已废弃** — `start.sh` 中已注释掉,但文件保留,不要删除
|
||||
|
||||
## COMMANDS
|
||||
|
||||
```bash
|
||||
# 本地构建并启动
|
||||
docker-compose up -d --build
|
||||
|
||||
# 仅启动(使用已有镜像)
|
||||
docker-compose up -d
|
||||
|
||||
# 查看日志
|
||||
docker-compose logs -f wechat-selkies
|
||||
|
||||
# 进入容器调试
|
||||
docker exec -it wechat-selkies bash
|
||||
|
||||
# 清理升级问题
|
||||
rm -rf ./config/.config/openbox
|
||||
```
|
||||
|
||||
## NOTES
|
||||
|
||||
- 端口 3000=HTTP, 3001=HTTPS(推荐 HTTPS)
|
||||
- `shm_size: "1gb"` 影响 WebRTC 性能,建议保留
|
||||
- `/dev/dri` 映射为可选 GPU 加速
|
||||
- `AUTO_START_WECHAT` / `AUTO_START_QQ` 控制容器启动时是否自动拉起应用
|
||||
- 第三方应用通过 proot-apps 安装,快捷方式自动出现在右键菜单
|
||||
- Docker Hub 推送需在 GitHub 仓库 Environment 中配置 `ENABLE_DOCKERHUB=true`
|
||||
@@ -27,7 +27,7 @@ RUN apt-get update && \
|
||||
libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \
|
||||
libxcomposite1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 \
|
||||
libxss1 libxtst6 libatomic1 libxcomposite1 libxrender1 libxrandr2 libxkbcommon-x11-0 \
|
||||
libfontconfig1 libdbus-1-3 libnss3 libx11-xcb1 python3-tk stalonetray
|
||||
libfontconfig1 libdbus-1-3 libnss3 libx11-xcb1 python3-tk stalonetray inotify-tools
|
||||
|
||||
RUN pip install --no-cache-dir python-xlib
|
||||
|
||||
|
||||
14
README.md
14
README.md
@@ -34,6 +34,7 @@
|
||||
- 🔧 **硬件加速**:可选的 GPU 硬件加速支持
|
||||
- 🪟 **窗口切换器**:左上角增加切换悬浮窗,方便切换到后台窗口,为后续添加其它功能做基础
|
||||
- 🤖 **自动启动**:可配置自动启动微信和QQ客户端(可选)
|
||||
- 📋 **桌面快捷方式集成**:自动扫描 `~/Desktop/` 下的 `.desktop` 文件并添加到右键菜单,方便启动第三方应用(如通过 proot-apps 安装的应用)
|
||||
|
||||
## 截图展示
|
||||

|
||||
@@ -159,6 +160,19 @@ docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri nickr
|
||||
|
||||
> **注意:** 如果升级后右键菜单缺少 `WeChat` 相关选项,请先清空本地挂载目录下的openbox目录(如`./config/.config/openbox`)。
|
||||
|
||||
## 安装第三方应用(如 Telegram)
|
||||
|
||||
本项目支持通过 [proot-apps](https://github.com/linuxserver/proot-apps) 安装第三方 Linux 应用。以 Telegram 为例:
|
||||
|
||||
1. 在浏览器中打开容器桌面
|
||||
2. 点击左侧 **侧边栏** → **应用程序**(Applications)
|
||||
3. 在应用列表中找到 **Telegram**
|
||||
4. 点击 **安装**(Install)按钮,等待安装完成
|
||||
|
||||
安装完成后,应用快捷方式会自动出现在 `~/Desktop/` 目录下,**右键菜单会自动刷新**,无需重启容器即可从菜单中启动该应用。
|
||||
|
||||
> **提示:** 如需卸载应用,同样通过侧边栏 → 应用程序,选中对应应用后点击 **卸载**(Uninstall)即可,右键菜单会自动更新。
|
||||
|
||||
## 高级配置
|
||||
|
||||
### 硬件加速
|
||||
|
||||
15
README_en.md
15
README_en.md
@@ -33,6 +33,8 @@ This project packages the official WeChat/QQ Linux client in a Docker container,
|
||||
- 🖥️ **AMD64 and ARM64 Architecture Support**: Compatible with mainstream CPU architectures
|
||||
- 🔧 **Hardware Acceleration**: Optional GPU hardware acceleration support
|
||||
- 🪟 **Window Switcher**: Added a floating window switcher in the top left corner for easy switching to background windows, laying the foundation for adding other features in the future
|
||||
- 🤖 **Auto Start**: Configurable auto-start for WeChat and QQ clients (optional)
|
||||
- 📋 **Desktop Shortcut Integration**: Automatically scans `.desktop` files in `~/Desktop/` and adds them to the right-click menu, making it easy to launch third-party applications (e.g., apps installed via proot-apps)
|
||||
|
||||
## Screenshots
|
||||

|
||||
@@ -156,6 +158,19 @@ Configure the following environment variables in `docker-compose.yml`:
|
||||
|
||||
> **Note:** If the right-click menu lacks `WeChat` related options after an upgrade, please clear the `openbox` directory in the local mounted directory (e.g., `./config/.config/openbox`).
|
||||
|
||||
## Installing Third-Party Applications (e.g., Telegram)
|
||||
|
||||
This project supports installing third-party Linux applications via [proot-apps](https://github.com/linuxserver/proot-apps). Here's how to install Telegram as an example:
|
||||
|
||||
1. Open the container desktop in your browser
|
||||
2. Click the **Sidebar** on the left → **Applications**
|
||||
3. Find **Telegram** in the application list
|
||||
4. Click the **Install** button and wait for the installation to complete
|
||||
|
||||
Once installed, the application shortcut will automatically appear in the `~/Desktop/` directory, and the **right-click menu will auto-refresh** — no container restart needed to launch the app from the menu.
|
||||
|
||||
> **Tip:** To uninstall an application, go to Sidebar → Applications, select the app, and click **Uninstall**. The right-click menu will update automatically.
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Hardware Acceleration
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<openbox_menu xmlns="http://openbox.org/3.4/menu">
|
||||
<menu id="root-menu" label="MENU">
|
||||
<item label="xterm" icon="/usr/share/pixmaps/xterm-color_48x48.xpm"><action name="Execute"><command>/usr/bin/xterm</command></action></item>
|
||||
<item label="WeChat" icon="/usr/share/icons/hicolor/128x128/apps/wechat.png"><action name="Execute"><command>/scripts/wechat/wechat-restart.sh</command></action></item>
|
||||
<item label="QQ" icon="/usr/share/icons/hicolor/512x512/apps/qq.png"><action name="Execute"><command>/scripts/qq/qq-restart.sh</command></action></item>
|
||||
<item label="WeChat" icon="/usr/share/icons/hicolor/128x128/apps/wechat.png"><action name="Execute"><command>/scripts/wechat/wechat-start.sh</command></action></item>
|
||||
<item label="QQ" icon="/usr/share/icons/hicolor/512x512/apps/qq.png"><action name="Execute"><command>/scripts/qq/qq-start.sh</command></action></item>
|
||||
</menu>
|
||||
</openbox_menu>
|
||||
|
||||
3
root/scripts/qq/qq-start.sh
Executable file
3
root/scripts/qq/qq-start.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
/usr/bin/qq --no-sandbox
|
||||
57
root/scripts/refresh-menu.sh
Executable file
57
root/scripts/refresh-menu.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
# Regenerate openbox right-click menu from defaults + ~/Desktop/*.desktop files
|
||||
|
||||
MENU_DEFAULT="/defaults/menu.xml"
|
||||
MENU_TARGET="/config/.config/openbox/menu.xml"
|
||||
MENU_TMP="/tmp/menu.xml"
|
||||
|
||||
mkdir -p /config/.config/openbox
|
||||
cp "$MENU_DEFAULT" "$MENU_TMP"
|
||||
|
||||
if ls "$HOME/Desktop/"*.desktop >/dev/null 2>&1; then
|
||||
for desktop_file in "$HOME/Desktop/"*.desktop; do
|
||||
name=$(grep -E "^Name=" "$desktop_file" | head -n 1 | cut -d "=" -f 2-)
|
||||
exec_cmd=$(grep -E "^Exec=" "$desktop_file" | head -n 1 | cut -d "=" -f 2-)
|
||||
icon=$(grep -E "^Icon=" "$desktop_file" | head -n 1 | cut -d "=" -f 2-)
|
||||
|
||||
# skip entries without a name or exec command
|
||||
[ -z "$name" ] || [ -z "$exec_cmd" ] && continue
|
||||
|
||||
# strip %U, %u, %F, %f field codes
|
||||
exec_cmd=$(echo "$exec_cmd" | sed 's/ %[fFuU]//g')
|
||||
|
||||
# resolve icon path if it's just a name (not an absolute path)
|
||||
if [ -n "$icon" ] && [ "${icon#/}" = "$icon" ]; then
|
||||
icon_resolved=""
|
||||
# search in proot-apps icons (prefer 256x256)
|
||||
for size in 256x256 512x512 128x128 64x64 48x48 scalable; do
|
||||
found=$(find /config/proot-apps/*/usr/share/icons/hicolor/"$size"/apps/"$icon".* 2>/dev/null | head -n 1)
|
||||
if [ -n "$found" ]; then
|
||||
icon_resolved="$found"
|
||||
break
|
||||
fi
|
||||
done
|
||||
# fallback: search system icons
|
||||
if [ -z "$icon_resolved" ]; then
|
||||
found=$(find /usr/share/icons/hicolor/*/apps/"$icon".* 2>/dev/null | head -n 1)
|
||||
[ -n "$found" ] && icon_resolved="$found"
|
||||
fi
|
||||
[ -n "$icon_resolved" ] && icon="$icon_resolved"
|
||||
fi
|
||||
|
||||
# handle missing icon
|
||||
[ -z "$icon" ] && icon="/usr/share/pixmaps/xterm-color_48x48.xpm"
|
||||
|
||||
# escape XML entities
|
||||
exec_cmd=$(echo "$exec_cmd" | sed 's/&/\&/g; s/</\</g; s/>/\>/g')
|
||||
name=$(echo "$name" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\"/g')
|
||||
icon=$(echo "$icon" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\"/g')
|
||||
|
||||
sed -i "/<menu id=\"root-menu\" label=\"MENU\">/a \\<item label=\"${name}\" icon=\"${icon}\"><action name=\"Execute\"><command>${exec_cmd}</command></action></item>" "$MENU_TMP"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ ! -f "$MENU_TARGET" ] || ! cmp -s "$MENU_TMP" "$MENU_TARGET"; then
|
||||
cp "$MENU_TMP" "$MENU_TARGET"
|
||||
openbox --reconfigure 2>/dev/null || true
|
||||
fi
|
||||
@@ -8,11 +8,16 @@ if [ ! -f /config/.config/openbox/rc.xml ] || grep -A20 "<dock>" /config/.config
|
||||
openbox --reconfigure
|
||||
fi
|
||||
|
||||
# update openbox menu if differs from default
|
||||
if [ ! -f /config/.config/openbox/menu.xml ] || ! cmp /defaults/menu.xml /config/.config/openbox/menu.xml; then
|
||||
mkdir -p /config/.config/openbox
|
||||
cp /defaults/menu.xml /config/.config/openbox/menu.xml
|
||||
openbox --reconfigure
|
||||
# generate openbox menu from defaults + ~/Desktop/*.desktop files
|
||||
/scripts/refresh-menu.sh
|
||||
|
||||
# watch ~/Desktop/ for .desktop file changes and auto-refresh menu
|
||||
mkdir -p "$HOME/Desktop"
|
||||
if command -v inotifywait >/dev/null 2>&1; then
|
||||
(while inotifywait -q -e create -e delete -e modify "$HOME/Desktop/" --include '\.desktop$'; do
|
||||
sleep 1
|
||||
/scripts/refresh-menu.sh
|
||||
done) >/dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
nohup stalonetray --dockapp-mode simple > /dev/null 2>&1 &
|
||||
|
||||
Reference in New Issue
Block a user