11 Commits

Author SHA1 Message Date
Nick007
ecc68281c3 fix: use start scripts instead of restart in menu to support multi-instance (#102) 2026-03-23 14:00:27 +08:00
Nick007
6d039cf06e docs: add AGENTS.md project knowledge base 2026-03-23 13:52:38 +08:00
Nick007
e18821f917 feat: auto-refresh menu on app install/uninstall and add third-party app docs
- Extract menu generation logic into standalone refresh-menu.sh script
- Use inotifywait to watch ~/Desktop/ for .desktop file changes and
  auto-refresh the openbox right-click menu on app install/uninstall
- Add inotify-tools to Dockerfile dependencies
- Add 'Installing Third-Party Applications' section to both READMEs
  documenting how to install apps like Telegram via sidebar panel
2026-02-28 10:06:08 +08:00
Nick007
c7fd1ec6d2 feat: auto-integrate desktop shortcuts into openbox right-click menu
- Scan .desktop files from ~/Desktop/ on startup and dynamically add
  them to the openbox menu (/config/.config/openbox/menu.xml)
- Resolve icon names to actual paths under /config/proot-apps/ with
  fallback to system icons, preferring 256x256 resolution
- Escape XML entities in name, exec command, and icon path
- Only reconfigure openbox when menu content actually changes
- Update README.md and README_en.md with new feature description
2026-02-28 10:00:07 +08:00
Nick007
10b676bdad Update QQ download URLs 2025-12-10 09:34:23 +08:00
Nick007
64f774377a Clarify env vars and add shm size recommendation 2025-12-10 09:28:23 +08:00
Nick007
3444018947 Merge pull request #52 from wray-lee/patch-1
Update QQ download URLs for newer versions
2025-11-09 21:00:22 +08:00
Wray
13695c11d4 Update QQ download URLs for newer versions 2025-11-07 17:01:09 +08:00
Nick007
6cc4267762 ci: update issue templates 2025-10-29 12:59:16 +08:00
Nick007
2dca2294eb Update issue templates 2025-10-27 13:40:33 +08:00
Nick007
de4205ec35 docs: update README and minor fix 2025-10-26 20:30:16 +08:00
18 changed files with 872 additions and 31 deletions

138
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,138 @@
name: 🐛 Bug Report
description: 创建一个错误报告来帮助我们改进项目
title: "[Bug]: "
labels: ["bug", "triage"]
assignees: []
body:
- type: markdown
attributes:
value: |
感谢您花时间填写错误报告!请提供详细信息以帮助我们诊断问题。
> **注意**: 请确保标题和描述足够详细,以便我们能够理解和重现问题。
**标题要求**: 请在页面顶部的标题框中填写简洁明了的问题描述最少10个字符例如"微信无法启动显示黑屏"
- type: textarea
id: bug-description
attributes:
label: 错误描述
description: 清晰简洁地描述遇到的错误最少30个字符
placeholder: 详细描述您遇到的问题...
validations:
required: true
- type: textarea
id: reproduction-steps
attributes:
label: 重现步骤
description: 详细说明重现该错误的步骤最少50个字符
placeholder: |
1. 执行 '...'
2. 点击 '...'
3. 滚动到 '...'
4. 看到错误
value: |
1.
2.
3.
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: 预期行为
description: 清楚简洁地描述您期望发生的情况
placeholder: 描述应该发生什么...
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: 截图
description: 如果适用,请添加截图来帮助解释问题
placeholder: 拖放图片或粘贴图片链接
- type: dropdown
id: os
attributes:
label: 操作系统
description: 您使用的操作系统
options:
- Ubuntu 20.04
- Ubuntu 22.04
- Ubuntu 24.04
- Debian 11
- Debian 12
- CentOS 7
- CentOS 8
- Rocky Linux 8
- Rocky Linux 9
- Windows 10
- Windows 11
- macOS
- 其他(请在附加信息中说明)
validations:
required: true
- type: dropdown
id: browser
attributes:
label: 浏览器
description: 您使用的浏览器
options:
- Chrome
- Firefox
- Safari
- Edge
- 其他(请在附加信息中说明)
validations:
required: true
- type: input
id: docker-version
attributes:
label: Docker版本
description: 运行 `docker --version` 的输出
placeholder: 例如Docker version 24.0.6
validations:
required: true
- type: input
id: compose-version
attributes:
label: Docker Compose版本
description: 运行 `docker-compose --version` 的输出
placeholder: 例如docker-compose version 2.21.0
validations:
required: true
- type: textarea
id: logs
attributes:
label: 相关日志
description: 请提供相关的容器日志或错误信息
placeholder: 粘贴 `docker logs <container-name>` 的输出
render: shell
- type: textarea
id: additional-context
attributes:
label: 附加信息
description: 添加关于该问题的任何其他相关信息
placeholder: 任何可能有助于解决问题的额外信息...
- type: checkboxes
id: terms
attributes:
label: 检查清单
description: 请确认以下事项
options:
- label: 我已经搜索了现有的issues确认这不是重复问题
required: true
- label: 我已经阅读了README和相关文档
required: true
- label: 我提供的信息足够详细,可以帮助重现问题
required: true

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: 📖 项目文档
url: https://github.com/nickrunning/wechat-selkies/blob/master/README.md
about: 请先查看项目文档和常见问题解答
- name: 💬 讨论区 - 问答交流
url: https://github.com/nickrunning/wechat-selkies/discussions
about: 一般性问题、经验分享、想法讨论请使用讨论区
- name: 📋 讨论区使用指南
url: https://github.com/nickrunning/wechat-selkies/blob/master/.github/discussions-guide.md
about: 了解讨论区表单使用方法和最佳实践
- name: 🔍 搜索现有内容
url: https://github.com/nickrunning/wechat-selkies/search?type=discussions
about: 搜索现有的讨论和问题,避免重复提交

View File

@@ -0,0 +1,112 @@
name: 🚀 Feature Request
description: 为这个项目建议一个新想法或功能
title: "[Feature]: "
labels: ["enhancement", "triage"]
assignees: []
body:
- type: markdown
attributes:
value: |
感谢您提出功能建议!请提供详细信息以帮助我们理解您的需求。
> **注意**: 请确保标题和描述足够详细,以便我们能够理解您的功能需求。
**标题要求**: 请在页面顶部的标题框中填写简洁明了的功能描述最少10个字符例如"添加微信多开功能支持"
- type: textarea
id: problem-description
attributes:
label: 问题描述
description: 清楚简洁地描述您遇到的问题或需求最少30个字符
placeholder: 例如:我希望能够同时运行多个微信实例...
validations:
required: true
- type: textarea
id: solution-description
attributes:
label: 解决方案描述
description: 清楚简洁地描述您希望实现的功能最少50个字符
placeholder: 详细描述您期望的功能如何工作...
validations:
required: true
- type: textarea
id: alternatives-considered
attributes:
label: 考虑的替代方案
description: 清楚简洁地描述您考虑过的任何替代解决方案或功能
placeholder: 描述您考虑过的其他方法...
validations:
required: true
- type: dropdown
id: feature-type
attributes:
label: 功能类型
description: 这个功能请求属于哪个类别?
options:
- 用户界面改进
- 性能优化
- 新功能
- 配置选项
- 文档改进
- 安全性增强
- 兼容性改进
- 其他
validations:
required: true
- type: dropdown
id: priority
attributes:
label: 优先级
description: 您认为这个功能的优先级如何?
options:
- 低 - 很有用,但不紧急
- 中 - 对工作流程有帮助
- 高 - 重要的功能缺失
- 紧急 - 阻塞性问题
validations:
required: true
- type: textarea
id: use-cases
attributes:
label: 使用场景
description: 描述这个功能的具体使用场景和用户故事
placeholder: |
作为用户,我希望能够...
这样我就可以...
例如:...
validations:
required: true
- type: textarea
id: mockups
attributes:
label: 模拟图或截图
description: 如果适用,请添加模拟图、线框图或参考截图
placeholder: 拖放图片或粘贴图片链接
- type: textarea
id: additional-context
attributes:
label: 附加信息
description: 添加关于功能请求的任何其他相关信息、链接或参考资料
placeholder: 任何可能有助于实现这个功能的额外信息...
- type: checkboxes
id: terms
attributes:
label: 检查清单
description: 请确认以下事项
options:
- label: 我已经搜索了现有的issues确认这不是重复的功能请求
required: true
- label: 我已经阅读了README和相关文档
required: true
- label: 这个功能请求与项目的目标和范围相符
required: true
- label: 我提供的描述足够详细,可以帮助理解功能需求
required: true

82
.github/ISSUE_TEMPLATE/general.yml vendored Normal file
View File

@@ -0,0 +1,82 @@
name: 📝 General Issue
description: 其他类型的问题或讨论
title: "[General]: "
labels: ["question", "triage"]
assignees: []
body:
- type: markdown
attributes:
value: |
如果您的问题不符合Bug报告或功能请求的类别请使用此模板。
> **提示**: 对于一般性讨论,考虑使用[讨论区](https://github.com/nickrunning/wechat-selkies/discussions)。
**标题要求**: 请在页面顶部的标题框中填写简洁明了的问题描述最少10个字符例如"关于项目配置的问题"
- type: dropdown
id: issue-type
attributes:
label: 问题类型
description: 请选择最符合您问题的类型
options:
- 使用问题/疑问
- 配置相关
- 安装/部署问题
- 文档问题
- 兼容性问题
- 建议/反馈
- 其他
validations:
required: true
- type: textarea
id: description
attributes:
label: 详细描述
description: 详细描述您的问题或情况最少30个字符
placeholder: 请详细描述您遇到的情况、需要的帮助或想要讨论的内容...
validations:
required: true
- type: textarea
id: context
attributes:
label: 相关背景
description: 提供相关的背景信息或上下文
placeholder: |
- 您在尝试做什么?
- 您的使用场景是什么?
- 是否有相关的配置或设置?
validations:
required: true
- type: textarea
id: environment
attributes:
label: 环境信息
description: 如果相关,请提供环境信息
placeholder: |
- 操作系统:
- Docker版本
- 浏览器:
- 其他相关信息:
- type: textarea
id: additional-info
attributes:
label: 附加信息
description: 任何其他可能相关的信息、链接或截图
placeholder: 包括日志、配置文件、截图等...
- type: checkboxes
id: checklist
attributes:
label: 检查清单
description: 请确认以下事项
options:
- label: 我已经搜索了现有的issues和讨论
required: true
- label: 我已经阅读了相关文档
required: true
- label: 我提供的信息足够详细
required: true

98
.github/workflows/cleanup-issues.yml vendored Normal file
View File

@@ -0,0 +1,98 @@
name: Close Invalid Issues
on:
schedule:
# 每天运行一次清理无效issues
- cron: '0 2 * * *'
workflow_dispatch: # 允许手动触发
jobs:
close-invalid-issues:
runs-on: ubuntu-latest
steps:
- name: Close stale issues with needs-more-info label
uses: actions/github-script@v6
with:
script: |
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'needs-more-info',
sort: 'updated',
direction: 'asc'
});
const now = new Date();
const staleThreshold = 7 * 24 * 60 * 60 * 1000; // 7天
for (const issue of issues) {
const updatedAt = new Date(issue.updated_at);
const daysSinceUpdate = Math.floor((now - updatedAt) / (24 * 60 * 60 * 1000));
if (now - updatedAt > staleThreshold) {
console.log(`Closing stale issue #${issue.number} (${daysSinceUpdate} days old)`);
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `🤖 **自动关闭通知**\n\n这个issue已经标记为需要更多信息超过7天且没有收到回复。为了保持issue列表的整洁现在自动关闭此issue。\n\n如果您仍然遇到问题\n1. 提供我们之前请求的信息\n2. 重新开启此issue或创建新的issue\n\n我们随时欢迎您提供更多详细信息`
});
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed',
state_reason: 'not_planned'
});
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ['auto-closed']
});
}
}
- name: Add warning to old issues without labels
uses: actions/github-script@v6
with:
script: |
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
sort: 'created',
direction: 'asc'
});
const now = new Date();
const oldThreshold = 3 * 24 * 60 * 60 * 1000; // 3天
for (const issue of issues) {
const createdAt = new Date(issue.created_at);
const isOld = now - createdAt > oldThreshold;
const hasTriageLabel = issue.labels.some(label => label.name === 'triage');
const hasNeedsInfoLabel = issue.labels.some(label => label.name === 'needs-more-info');
if (isOld && hasTriageLabel && !hasNeedsInfoLabel) {
console.log(`Adding reminder to issue #${issue.number}`);
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `⏰ **提醒**\n\n这个issue已经开放3天了但还没有被标记或分类。\n\n**维护者请注意:**\n- 请审查此issue并添加适当的标签\n- 如果需要更多信息,请添加 \`needs-more-info\` 标签\n- 如果是有效的bug或功能请求请移除 \`triage\` 标签并添加适当的优先级标签\n\n**提交者请注意:**\n- 如果您有更多信息可以补充,请随时添加\n- 确保您已经提供了足够的详细信息来重现问题或理解功能需求`
});
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ['stale']
});
}
}

View File

@@ -117,25 +117,27 @@ jobs:
**GitHub Container Registry:**
```bash
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
```
**Docker Hub** (if enabled):
```bash
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ github.ref_name }}
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ steps.meta.outputs.version }}
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest
```
## 🚀 Quick Start
**GitHub Container Registry:**
```bash
docker run -it -p 3001:3001 -v ./config:/config ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
```
**Docker Hub** (if enabled):
```bash
docker run -it -p 3001:3001 -v ./config:/config ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ github.ref_name }}
docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ steps.meta.outputs.version }}
docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest
```
Then visit: https://localhost:3001

189
.github/workflows/issue-validation.yml vendored Normal file
View File

@@ -0,0 +1,189 @@
name: Issue Validation
on:
issues:
types: [opened, edited]
jobs:
validate-issue:
runs-on: ubuntu-latest
steps:
- name: Check Issue Title Length
uses: actions/github-script@v6
with:
script: |
const issue = context.payload.issue;
const title = issue.title.trim();
const minTitleLength = 10;
const maxTitleLength = 120;
console.log(`Issue title: "${title}"`);
console.log(`Title length: ${title.length}`);
// 去除模板前缀进行长度检查
const cleanTitle = title.replace(/^\[(Bug|Feature|General)\]:\s*/i, '').trim();
if (cleanTitle.length < minTitleLength) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `⚠️ **标题过短**\n\n您的issue标题内容太短了当前有效内容${cleanTitle.length}个字符,最少需要${minTitleLength}个字符)。请提供一个更具描述性的标题,以便我们更好地理解问题。\n\n**示例:**\n- ✅ "[Bug]: 微信无法启动显示黑屏"\n- ❌ "[Bug]: 有问题"\n\n请编辑issue标题提供更多详细信息。`
});
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ['needs-more-info']
});
} else if (title.length > maxTitleLength) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `⚠️ **标题过长**\n\n您的issue标题太长了当前${title.length}个字符,建议不超过${maxTitleLength}个字符)。请简化标题,将详细信息移至描述部分。`
});
}
- name: Check Issue Body Length
uses: actions/github-script@v6
with:
script: |
const issue = context.payload.issue;
const body = issue.body ? issue.body.trim() : '';
const minBodyLength = 50;
console.log(`Issue body length: ${body.length}`);
if (body.length < minBodyLength) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `⚠️ **描述过短**\n\n您的issue描述太简短了当前${body.length}个字符,最少需要${minBodyLength}个字符)。为了帮助我们更好地理解和解决问题,请提供以下信息:\n\n- 详细的问题描述\n- 重现步骤\n- 预期行为\n- 实际行为\n- 环境信息操作系统、浏览器、Docker版本等\n- 相关日志或错误信息\n\n请编辑issue描述添加更多详细信息。`
});
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ['needs-more-info']
});
}
- name: Check for Template Usage
uses: actions/github-script@v6
with:
script: |
const issue = context.payload.issue;
const body = issue.body || '';
// 检查是否包含模板中的占位符文本(表明用户没有填写完整)
const templatePlaceholders = [
'例如:',
'placeholder',
'...',
'请描述',
'请填写',
'请提供'
];
const hasPlaceholders = templatePlaceholders.some(placeholder =>
body.toLowerCase().includes(placeholder)
);
if (hasPlaceholders) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `⚠️ **模板未完整填写**\n\n看起来您可能没有完全填写issue模板。请确保\n\n- 删除所有占位符文本和示例\n- 填写所有必需的字段\n- 提供真实的、具体的信息\n\n这将帮助我们更快地理解和解决您的问题。`
});
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ['needs-more-info']
});
}
- name: Auto-assign Labels Based on Content
uses: actions/github-script@v6
with:
script: |
const issue = context.payload.issue;
const title = issue.title.toLowerCase();
const body = (issue.body || '').toLowerCase();
const content = title + ' ' + body;
const labelRules = [
{
keywords: ['docker', 'dockerfile', 'compose', '容器'],
label: 'docker'
},
{
keywords: ['微信', 'wechat', '登录', 'login'],
label: 'wechat'
},
{
keywords: ['网络', 'network', 'proxy', '代理', 'ssl', 'https'],
label: 'network'
},
{
keywords: ['性能', 'performance', 'slow', '慢', 'memory', '内存'],
label: 'performance'
},
{
keywords: ['音频', 'audio', '声音', 'sound'],
label: 'audio'
},
{
keywords: ['视频', 'video', '显示', 'display'],
label: 'video'
},
{
keywords: ['文档', 'documentation', 'readme', 'doc'],
label: 'documentation'
}
];
const labelsToAdd = [];
for (const rule of labelRules) {
if (rule.keywords.some(keyword => content.includes(keyword))) {
labelsToAdd.push(rule.label);
}
}
if (labelsToAdd.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: labelsToAdd
});
}
- name: Welcome First Time Contributors
uses: actions/github-script@v6
with:
script: |
const issue = context.payload.issue;
// 检查是否是首次提交issue的用户
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
creator: issue.user.login,
state: 'all'
});
if (issues.length === 1) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `🎉 **欢迎首次贡献!**\n\n感谢您提交第一个issue我们很高兴您选择使用并改进这个项目。\n\n**接下来会发生什么:**\n- 维护者将审查您的issue\n- 如果需要更多信息,我们会在评论中询问\n- 我们会尽快回复并提供帮助\n\n**有用的资源:**\n- [项目文档](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/master/README.md)\n- [讨论区](https://github.com/${context.repo.owner}/${context.repo.repo}/discussions)\n\n再次感谢您的贡献 🙏`
});
}

94
AGENTS.md Normal file
View 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`

View File

@@ -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
@@ -54,10 +54,10 @@ RUN case "$TARGETPLATFORM" in \
# Install QQ based on target architecture
RUN case "$TARGETPLATFORM" in \
"linux/amd64") \
QQ_URL="https://dldir1v6.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.19_250904_amd64_01.deb"; \
QQ_URL="https://dldir1v6.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.22_251203_amd64_01.deb"; \
QQ_ARCH="x86_64" ;; \
"linux/arm64") \
QQ_URL="https://dldir1v6.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.19_250904_arm64_01.deb"; \
QQ_URL="https://dldir1v6.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.22_251203_arm64_01.deb"; \
QQ_ARCH="arm64" ;; \
*) \
echo "❌ Unsupported platform: $TARGETPLATFORM" >&2; \

View File

@@ -34,6 +34,11 @@
- 🔧 **硬件加速**:可选的 GPU 硬件加速支持
- 🪟 **窗口切换器**:左上角增加切换悬浮窗,方便切换到后台窗口,为后续添加其它功能做基础
- 🤖 **自动启动**可配置自动启动微信和QQ客户端可选
- 📋 **桌面快捷方式集成**:自动扫描 `~/Desktop/` 下的 `.desktop` 文件并添加到右键菜单,方便启动第三方应用(如通过 proot-apps 安装的应用)
## 截图展示
![微信截图](./docs/images/wechat-selkies-1.jpg)
![QQ截图](./docs/images/wechat-selkies-2.jpg)
## 快速开始
@@ -49,17 +54,18 @@
GitHub Container Registry镜像
```bash
docker run -it -p 3001:3001 -v ./config:/config ghcr.io/nickrunning/wechat-selkies:latest
docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri ghcr.io/nickrunning/wechat-selkies:latest
```
Docker Hub镜像
```bash
docker run -it -p 3001:3001 -v ./config:/config nickrunning/wechat-selkies:latest
docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri nickrunning/wechat-selkies:latest
```
2. **访问微信**
在浏览器中访问:`https://localhost:3001``https://<服务器IP>:3001`
> **注意:** 映射3000端口用于HTTP访问3001端口用于HTTPS访问建议使用HTTPS。
### docker-compose 部署
1. **创建项目目录并进入**
@@ -74,20 +80,23 @@ docker run -it -p 3001:3001 -v ./config:/config nickrunning/wechat-selkies:lates
image: nickrunning/wechat-selkies:latest # or ghcr.io/nickrunning/wechat-selkies:latest
container_name: wechat-selkies
ports:
- "3001:3001"
- "3000:3000" # http port
- "3001:3001" # https port
restart: unless-stopped
volumes:
- ./config:/config
devices:
- /dev/dri:/dev/dri # optional, for hardware acceleration
environment:
- PUID=1000 # user ID
- PGID=100 # group ID
- TZ=Asia/Shanghai # timezone
- LC_ALL=zh_CN.UTF-8 # locale
- PUID=1000 # user ID, set according to your system
- PGID=100 # group ID, set according to your system
- TZ=Asia/Shanghai # timezone, set according to your timezone
- LC_ALL=zh_CN.UTF-8 # locale, set according to your needs
- AUTO_START_WECHAT=true # default is true
- AUTO_START_QQ=false # default is false
# - CUSTOM_USER=<Your Name> # recommended to set a custom user name
# - PASSWORD=<Your Password> # recommended to set a password for selkies web ui
shm_size: "1gb" # recommended, will improve performance
```
3. **启动服务**
```bash
@@ -151,6 +160,19 @@ docker run -it -p 3001:3001 -v ./config:/config nickrunning/wechat-selkies:lates
> **注意:** 如果升级后右键菜单缺少 `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即可右键菜单会自动更新。
## 高级配置
### 硬件加速

View File

@@ -33,6 +33,12 @@ 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
![WeChat Screenshot](./docs/images/wechat-selkies-1.jpg)
![QQ Screenshot](./docs/images/wechat-selkies-2.jpg)
## Quick Start
@@ -47,16 +53,17 @@ This project packages the official WeChat/QQ Linux client in a Docker container,
1. **Direct deployment using pre-built images**
GitHub Container Registry image:
```bash
docker run -it -p 3001:3001 -v ./config:/config ghcr.io/nickrunning/wechat-selkies:latest
docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri ghcr.io/nickrunning/wechat-selkies:latest
```
Docker Hub image:
```bash
docker run -it -p 3001:3001 -v ./config:/config nickrunning/wechat-selkies:latest
docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri nickrunning/wechat-selkies:latest
```
2. **Access WeChat**
Open in browser: `https://localhost:3001` or `https://<server-ip>:3001`
> **Note**: 3001 port is for HTTPS access. If you need HTTP access, please map port 3000 as well.
### docker-compose Deployment
1. **Create project directory and navigate into it**
@@ -71,20 +78,23 @@ docker run -it -p 3001:3001 -v ./config:/config nickrunning/wechat-selkies:lates
image: nickrunning/wechat-selkies:latest # or ghcr.io/nickrunning/wechat-selkies:latest
container_name: wechat-selkies
ports:
- "3001:3001"
- "3000:3000" # http port
- "3001:3001" # https port
restart: unless-stopped
volumes:
- ./config:/config
devices:
- /dev/dri:/dev/dri # optional, for hardware acceleration
environment:
- PUID=1000 # user ID
- PGID=100 # group ID
- TZ=Asia/Shanghai # timezone
- LC_ALL=zh_CN.UTF-8 # locale
- PUID=1000 # user ID, set according to your system
- PGID=100 # group ID, set according to your system
- TZ=Asia/Shanghai # timezone, set according to your timezone
- LC_ALL=zh_CN.UTF-8 # locale, set according to your needs
- AUTO_START_WECHAT=true # default is true
- AUTO_START_QQ=false # default is false
# - CUSTOM_USER=<Your Name> # recommended to set a custom user name
# - PASSWORD=<Your Password> # recommended to set a password for selkies web ui
shm_size: "1gb" # recommended, will improve performance
```
3. **Start the service**
```bash
@@ -148,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

View File

@@ -10,10 +10,12 @@ services:
devices:
- /dev/dri:/dev/dri # optional, for hardware acceleration
ports:
- "3001:3001"
- "3000:3000" # http port
- "3001:3001" # https port
restart: unless-stopped
environment:
- PUID=1000
- PGID=100
- PUID=1000 # set user id according to your system
- PGID=100 # set group id according to your system
# - CUSTOM_USER=<Your Name> # recommended to set a custom user name
# - PASSWORD=<Your Password> # recommended to set a password for selkies web ui
shm_size: "1gb" # recommended, will improve performance

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -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
View File

@@ -0,0 +1,3 @@
#!/bin/bash
/usr/bin/qq --no-sandbox

57
root/scripts/refresh-menu.sh Executable file
View 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/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
name=$(echo "$name" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g')
icon=$(echo "$icon" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/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

View File

@@ -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 &