mirror of
https://github.com/nickrunning/wechat-selkies.git
synced 2026-05-09 00:24:09 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ab51c11b0 | ||
|
|
179f50f45f | ||
|
|
35791ff5d3 | ||
|
|
a5f832dd09 | ||
|
|
77edc4b33e | ||
|
|
680ee6dd4a | ||
|
|
63b3f34527 | ||
|
|
c2b6106fff | ||
|
|
e42a8e861d | ||
|
|
d87cb16fb9 | ||
|
|
7c13e87a69 | ||
|
|
ecc68281c3 | ||
|
|
6d039cf06e | ||
|
|
e18821f917 | ||
|
|
c7fd1ec6d2 | ||
|
|
10b676bdad | ||
|
|
64f774377a | ||
|
|
3444018947 | ||
|
|
13695c11d4 | ||
|
|
6cc4267762 | ||
|
|
2dca2294eb | ||
|
|
de4205ec35 |
14
.env.example
Normal file
14
.env.example
Normal file
@@ -0,0 +1,14 @@
|
||||
# Ports
|
||||
HTTP_PORT=3000
|
||||
HTTPS_PORT=3001
|
||||
|
||||
# User/Group
|
||||
PUID=1000
|
||||
PGID=100
|
||||
|
||||
# Selkies Web UI
|
||||
# CUSTOM_USER=
|
||||
# PASSWORD=
|
||||
|
||||
# Shared Memory
|
||||
SHM_SIZE=1gb
|
||||
138
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
138
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal 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
14
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal 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: 搜索现有的讨论和问题,避免重复提交
|
||||
112
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
112
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal 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
82
.github/ISSUE_TEMPLATE/general.yml
vendored
Normal 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
|
||||
125
.github/scripts/detect-upstream.sh
vendored
Executable file
125
.github/scripts/detect-upstream.sh
vendored
Executable file
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
STATE_FILE="${STATE_FILE:-versions/upstream.env}"
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
CHANGE_DETECTED="false"
|
||||
ENV_WECHAT_AMD64_URL="${WECHAT_AMD64_URL-__UNSET__}"
|
||||
ENV_WECHAT_ARM64_URL="${WECHAT_ARM64_URL-__UNSET__}"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ -f "$STATE_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$STATE_FILE"
|
||||
fi
|
||||
|
||||
if [[ "$ENV_WECHAT_AMD64_URL" != "__UNSET__" ]]; then
|
||||
WECHAT_AMD64_URL="$ENV_WECHAT_AMD64_URL"
|
||||
fi
|
||||
|
||||
if [[ "$ENV_WECHAT_ARM64_URL" != "__UNSET__" ]]; then
|
||||
WECHAT_ARM64_URL="$ENV_WECHAT_ARM64_URL"
|
||||
fi
|
||||
|
||||
WECHAT_AMD64_URL="${WECHAT_AMD64_URL:-https://dldir1v6.qq.com/weixin/Universal/Linux/WeChatLinux_x86_64.deb}"
|
||||
WECHAT_ARM64_URL="${WECHAT_ARM64_URL:-https://dldir1v6.qq.com/weixin/Universal/Linux/WeChatLinux_arm64.deb}"
|
||||
|
||||
download_package() {
|
||||
local source_path="$1"
|
||||
local destination="$2"
|
||||
|
||||
case "$source_path" in
|
||||
http://*|https://*)
|
||||
curl --fail --silent --show-error --location \
|
||||
--retry 3 --retry-delay 5 --retry-all-errors \
|
||||
-o "$destination" "$source_path"
|
||||
;;
|
||||
*)
|
||||
cp "$source_path" "$destination"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
read_metadata() {
|
||||
local package_name="$1"
|
||||
local arch="$2"
|
||||
local source_path="$3"
|
||||
local package_path="$TMP_DIR/${package_name}-${arch}.deb"
|
||||
local version_var="${package_name}_${arch}_VERSION"
|
||||
local sha_var="${package_name}_${arch}_SHA256"
|
||||
local current_version="${!version_var:-}"
|
||||
local current_sha="${!sha_var:-}"
|
||||
local detected_version
|
||||
local detected_sha
|
||||
|
||||
echo "Checking ${package_name} ${arch} from ${source_path}"
|
||||
download_package "$source_path" "$package_path"
|
||||
|
||||
detected_version="$(dpkg-deb -f "$package_path" Version)"
|
||||
detected_sha="$(sha256sum "$package_path" | awk '{print $1}')"
|
||||
|
||||
printf -v "$version_var" '%s' "$detected_version"
|
||||
printf -v "$sha_var" '%s' "$detected_sha"
|
||||
|
||||
if [[ "$current_version" != "$detected_version" || "$current_sha" != "$detected_sha" ]]; then
|
||||
CHANGE_DETECTED="true"
|
||||
fi
|
||||
}
|
||||
|
||||
read_metadata "WECHAT" "AMD64" "$WECHAT_AMD64_URL"
|
||||
read_metadata "WECHAT" "ARM64" "$WECHAT_ARM64_URL"
|
||||
|
||||
if [[ "$CHANGE_DETECTED" == "true" || ! -f "$STATE_FILE" ]]; then
|
||||
WECHAT_LAST_CHECKED_AT="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
fi
|
||||
|
||||
NEW_STATE_FILE="$TMP_DIR/upstream.env"
|
||||
cat > "$NEW_STATE_FILE" <<EOF
|
||||
# Upstream package state tracked by automation.
|
||||
|
||||
WECHAT_AMD64_URL="$WECHAT_AMD64_URL"
|
||||
WECHAT_ARM64_URL="$WECHAT_ARM64_URL"
|
||||
|
||||
WECHAT_AMD64_VERSION="$WECHAT_AMD64_VERSION"
|
||||
WECHAT_ARM64_VERSION="$WECHAT_ARM64_VERSION"
|
||||
|
||||
WECHAT_AMD64_SHA256="$WECHAT_AMD64_SHA256"
|
||||
WECHAT_ARM64_SHA256="$WECHAT_ARM64_SHA256"
|
||||
|
||||
WECHAT_LAST_CHECKED_AT="$WECHAT_LAST_CHECKED_AT"
|
||||
EOF
|
||||
|
||||
mkdir -p "$(dirname "$STATE_FILE")"
|
||||
if [[ ! -f "$STATE_FILE" ]] || ! cmp -s "$NEW_STATE_FILE" "$STATE_FILE"; then
|
||||
cp "$NEW_STATE_FILE" "$STATE_FILE"
|
||||
fi
|
||||
|
||||
echo "Change detected: $CHANGE_DETECTED"
|
||||
echo "WECHAT_AMD64_VERSION=$WECHAT_AMD64_VERSION"
|
||||
echo "WECHAT_ARM64_VERSION=$WECHAT_ARM64_VERSION"
|
||||
|
||||
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
|
||||
{
|
||||
echo "changed=$CHANGE_DETECTED"
|
||||
echo "wechat_amd64_version=$WECHAT_AMD64_VERSION"
|
||||
echo "wechat_arm64_version=$WECHAT_ARM64_VERSION"
|
||||
echo "state_file=$STATE_FILE"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
|
||||
{
|
||||
echo "## Upstream Detection"
|
||||
echo ""
|
||||
echo "- Changed: \`$CHANGE_DETECTED\`"
|
||||
echo "- WeChat amd64: \`$WECHAT_AMD64_VERSION\`"
|
||||
echo "- WeChat arm64: \`$WECHAT_ARM64_VERSION\`"
|
||||
echo "- State file: \`$STATE_FILE\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
98
.github/workflows/cleanup-issues.yml
vendored
Normal file
98
.github/workflows/cleanup-issues.yml
vendored
Normal 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']
|
||||
});
|
||||
}
|
||||
}
|
||||
32
.github/workflows/detect-upstream.yml
vendored
Normal file
32
.github/workflows/detect-upstream.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Detect Upstream Package Updates
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '23 */6 * * *'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
detect-wechat-updates:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Detect upstream package changes
|
||||
id: detect
|
||||
run: ./.github/scripts/detect-upstream.sh
|
||||
|
||||
- name: Commit updated version state
|
||||
if: steps.detect.outputs.changed == 'true'
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add versions/upstream.env
|
||||
git commit -m "chore: update upstream package state"
|
||||
git push
|
||||
64
.github/workflows/docker.yml
vendored
64
.github/workflows/docker.yml
vendored
@@ -3,6 +3,10 @@ name: Build and Publish Docker Image
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'versions/upstream.env'
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
@@ -70,6 +74,37 @@ jobs:
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Extract metadata for minimal image
|
||||
id: meta-minimal
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/${{ github.repository }}
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }},enable=${{ vars.ENABLE_DOCKERHUB == 'true' }}
|
||||
flavor: |
|
||||
latest=false
|
||||
suffix=-minimal
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=raw,value=minimal,enable=${{ github.ref == format('refs/heads/{0}', 'master') || startsWith(github.ref, 'refs/tags/') }}
|
||||
|
||||
- name: Build and push minimal Docker image
|
||||
id: build-minimal
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta-minimal.outputs.tags }}
|
||||
labels: ${{ steps.meta-minimal.outputs.labels }}
|
||||
build-args: |
|
||||
INSTALL_QQ=false
|
||||
INSTALL_PCMANFM=false
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Generate artifact attestation
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
uses: actions/attest-build-provenance@v1
|
||||
@@ -84,11 +119,16 @@ jobs:
|
||||
echo "### GitHub Container Registry" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Registry:** ${{ env.REGISTRY }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Repository:** ${{ env.IMAGE_NAME }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Digest:** \`${{ steps.build-image.outputs.digest }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Tags:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Full image digest:** \`${{ steps.build-image.outputs.digest }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Minimal image digest:** \`${{ steps.build-minimal.outputs.digest }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Tags (full):**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Tags (minimal):**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "${{ steps.meta-minimal.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ "${{ vars.ENABLE_DOCKERHUB }}" == "true" ]; then
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -115,27 +155,33 @@ jobs:
|
||||
body: |
|
||||
## 🐳 Docker Images
|
||||
|
||||
### Full (WeChat + QQ + File Manager)
|
||||
**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:**
|
||||
### Minimal (WeChat only)
|
||||
```bash
|
||||
docker run -it -p 3001:3001 -v ./config:/config ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:minimal
|
||||
```
|
||||
|
||||
**Docker Hub** (if enabled):
|
||||
## 🚀 Quick Start
|
||||
**Full:**
|
||||
```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 ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
```
|
||||
|
||||
**Minimal:**
|
||||
```bash
|
||||
docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:minimal
|
||||
```
|
||||
|
||||
Then visit: https://localhost:3001
|
||||
189
.github/workflows/issue-validation.yml
vendored
Normal file
189
.github/workflows/issue-validation.yml
vendored
Normal 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再次感谢您的贡献! 🙏`
|
||||
});
|
||||
}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/config/
|
||||
.env
|
||||
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`
|
||||
30
Dockerfile
30
Dockerfile
@@ -13,9 +13,10 @@ LABEL org.opencontainers.image.licenses="GPL-3.0-only"
|
||||
# Build arguments for multi-arch support
|
||||
ARG TARGETPLATFORM
|
||||
ARG BUILDPLATFORM
|
||||
ARG INSTALL_QQ=true
|
||||
ARG INSTALL_PCMANFM=true
|
||||
RUN echo "🏗️ Building WeChat-Selkies on $BUILDPLATFORM, targeting $TARGETPLATFORM"
|
||||
|
||||
# set environment variables
|
||||
RUN apt-get update && \
|
||||
apt-get install -y fonts-noto-cjk libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
|
||||
libxcb-render-util0 libxcb-xkb1 libxkbcommon-x11-0 \
|
||||
@@ -27,7 +28,12 @@ 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 stalonetray inotify-tools
|
||||
|
||||
ARG INSTALL_PCMANFM
|
||||
RUN if [ "$INSTALL_PCMANFM" = "true" ]; then \
|
||||
apt-get install -y --no-install-recommends pcmanfm; \
|
||||
fi
|
||||
|
||||
RUN pip install --no-cache-dir python-xlib
|
||||
|
||||
@@ -45,31 +51,35 @@ RUN case "$TARGETPLATFORM" in \
|
||||
exit 1 ;; \
|
||||
esac && \
|
||||
echo "📦 Downloading WeChat for $WECHAT_ARCH architecture..." && \
|
||||
curl -fsSL -o wechat.deb "$WECHAT_URL" && \
|
||||
curl -fsSL --retry 3 --retry-delay 10 --retry-all-errors -o wechat.deb "$WECHAT_URL" && \
|
||||
echo "🔧 Installing WeChat..." && \
|
||||
(dpkg -i wechat.deb || (apt-get update && apt-get install -f -y && dpkg -i wechat.deb)) && \
|
||||
rm -f wechat.deb && \
|
||||
echo "✅ WeChat installation completed for $WECHAT_ARCH"
|
||||
|
||||
# Install QQ based on target architecture
|
||||
RUN case "$TARGETPLATFORM" in \
|
||||
# Install QQ based on target architecture (optional)
|
||||
ARG INSTALL_QQ
|
||||
RUN if [ "$INSTALL_QQ" = "true" ]; then \
|
||||
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.27_260401_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.27_260401_arm64_01.deb"; \
|
||||
QQ_ARCH="arm64" ;; \
|
||||
*) \
|
||||
echo "❌ Unsupported platform: $TARGETPLATFORM" >&2; \
|
||||
echo "Supported platforms: linux/amd64, linux/arm64" >&2; \
|
||||
exit 1 ;; \
|
||||
esac && \
|
||||
echo "📦 Downloading QQ for $QQ_ARCH architecture..." && \
|
||||
curl -fsSL -o qq.deb "$QQ_URL" && \
|
||||
curl -fsSL --retry 3 --retry-delay 10 --retry-all-errors -o qq.deb "$QQ_URL" && \
|
||||
echo "🔧 Installing QQ..." && \
|
||||
(dpkg -i qq.deb || (apt-get update && apt-get install -f -y && dpkg -i qq.deb)) && \
|
||||
rm -f qq.deb && \
|
||||
echo "✅ QQ installation completed for $QQ_ARCH"
|
||||
echo "✅ QQ installation completed for $QQ_ARCH"; \
|
||||
else \
|
||||
echo "⏭️ Skipping QQ installation (INSTALL_QQ=$INSTALL_QQ)"; \
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
RUN apt-get purge -y --autoremove
|
||||
|
||||
116
README.md
116
README.md
@@ -22,6 +22,8 @@
|
||||
|
||||
> 如果升级后部分功能缺失,请先清空本地挂载目录下的openbox目录(如`./config/.config/openbox`)。
|
||||
|
||||
> 仓库内置了上游微信版本自动检测机制:GitHub Actions 会定时检查官方 `.deb` 包版本,检测到变化后自动更新 `versions/upstream.env` 并触发镜像构建。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🌐 **浏览器访问**:通过 Web 浏览器直接使用微信,无需本地安装
|
||||
@@ -34,6 +36,12 @@
|
||||
- 🔧 **硬件加速**:可选的 GPU 硬件加速支持
|
||||
- 🪟 **窗口切换器**:左上角增加切换悬浮窗,方便切换到后台窗口,为后续添加其它功能做基础
|
||||
- 🤖 **自动启动**:可配置自动启动微信和QQ客户端(可选)
|
||||
- 📋 **桌面快捷方式集成**:自动扫描 `~/Desktop/` 下的 `.desktop` 文件并添加到右键菜单,方便启动第三方应用(如通过 proot-apps 安装的应用)
|
||||
- 📂 **文件管理器**:内置 PCManFM 轻量文件管理器,右键菜单即可启动,方便管理容器内文件
|
||||
|
||||
## 截图展示
|
||||

|
||||

|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -49,19 +57,26 @@
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
> **精简版镜像**:如果只需要微信(不含 QQ 和文件管理器),可使用 `minimal` 标签,镜像体积更小:
|
||||
> ```bash
|
||||
> docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri ghcr.io/nickrunning/wechat-selkies:minimal
|
||||
> ```
|
||||
> 精简版也支持版本号标签,如 `:1.2.3-minimal`、`:1.2-minimal`,方便锁定特定版本。
|
||||
|
||||
2. **访问微信**
|
||||
|
||||
在浏览器中访问:`https://localhost:3001` 或 `https://<服务器IP>:3001`
|
||||
> **注意:** 映射3000端口用于HTTP访问,3001端口用于HTTPS访问,建议使用HTTPS。
|
||||
|
||||
### docker-compose 部署
|
||||
### Docker Compose 部署
|
||||
1. **创建项目目录并进入**
|
||||
```bash
|
||||
mkdir wechat-selkies
|
||||
@@ -74,24 +89,43 @@ 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"
|
||||
- "${HTTP_PORT:-3000}:3000"
|
||||
- "${HTTPS_PORT:-3001}:3001"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/config
|
||||
devices:
|
||||
- /dev/dri:/dev/dri # optional, for hardware acceleration
|
||||
- /dev/dri:/dev/dri
|
||||
environment:
|
||||
- PUID=1000 # user ID
|
||||
- PGID=100 # group ID
|
||||
- TZ=Asia/Shanghai # timezone
|
||||
- LC_ALL=zh_CN.UTF-8 # locale
|
||||
- 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
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-100}
|
||||
- TZ=Asia/Shanghai
|
||||
- LC_ALL=zh_CN.UTF-8
|
||||
- AUTO_START_WECHAT=true
|
||||
- AUTO_START_QQ=false
|
||||
- CUSTOM_USER=${CUSTOM_USER:-}
|
||||
- PASSWORD=${PASSWORD:-}
|
||||
shm_size: "${SHM_SIZE:-1gb}"
|
||||
```
|
||||
3. **启动服务**
|
||||
3. **创建 `.env` 文件(可选)**
|
||||
|
||||
复制 `.env.example` 并按需修改,未设置的变量将使用默认值:
|
||||
```bash
|
||||
docker-compose up -d
|
||||
cp .env.example .env
|
||||
```
|
||||
`.env` 文件示例:
|
||||
```env
|
||||
HTTP_PORT=3000
|
||||
HTTPS_PORT=3001
|
||||
PUID=1000
|
||||
PGID=100
|
||||
# CUSTOM_USER=
|
||||
# PASSWORD=
|
||||
SHM_SIZE=1gb
|
||||
```
|
||||
4. **启动服务**
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 源码部署
|
||||
@@ -104,13 +138,18 @@ docker run -it -p 3001:3001 -v ./config:/config nickrunning/wechat-selkies:lates
|
||||
|
||||
2. **启动服务**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
3. **访问微信**
|
||||
|
||||
在浏览器中访问:`https://localhost:3001` 或 `https://<服务器IP>:3001`
|
||||
|
||||
> **构建精简版**:源码部署时可通过 build-arg 构建仅含微信的精简镜像:
|
||||
> ```bash
|
||||
> docker build --build-arg INSTALL_QQ=false --build-arg INSTALL_PCMANFM=false -t wechat-selkies:minimal .
|
||||
> ```
|
||||
|
||||
### 配置说明
|
||||
|
||||
更多自定义配置请参考 [Selkies Base Images from LinuxServer](https://github.com/linuxserver/docker-baseimage-selkies)。
|
||||
@@ -127,7 +166,7 @@ docker run -it -p 3001:3001 -v ./config:/config nickrunning/wechat-selkies:lates
|
||||
|
||||
#### 环境变量配置
|
||||
|
||||
在 `docker-compose.yml` 中可以配置以下环境变量:
|
||||
在 `docker-compose.yml` 中可以配置以下环境变量,支持通过 `.env` 文件覆盖带有 `${VAR:-default}` 的配置项:
|
||||
|
||||
| 变量名 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
@@ -151,6 +190,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)即可,右键菜单会自动更新。
|
||||
|
||||
## 高级配置
|
||||
|
||||
### 硬件加速
|
||||
@@ -167,6 +219,7 @@ devices:
|
||||
```
|
||||
wechat-selkies/
|
||||
├── docker-compose.yml # Docker Compose 配置文件
|
||||
├── .env.example # 环境变量示例文件
|
||||
├── Dockerfile # Docker 镜像构建文件
|
||||
├── LICENSE # License
|
||||
├── README.md # 项目说明文档
|
||||
@@ -179,6 +232,33 @@ wechat-selkies/
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 更新微信/QQ版本
|
||||
|
||||
当微信或QQ提示"版本过期"时,只需重新拉取最新镜像并重建容器即可,聊天记录和配置不受影响:
|
||||
|
||||
```bash
|
||||
# 使用预构建镜像
|
||||
docker compose pull && docker compose up -d
|
||||
|
||||
# 使用源码构建
|
||||
git pull && docker compose up -d --build
|
||||
```
|
||||
|
||||
> **注意:** 微信和QQ的安装包 URL 指向官方最新版本,重新构建镜像时会自动下载最新版。
|
||||
|
||||
对于仓库维护者,当前自动化流程如下:
|
||||
|
||||
1. `Detect Upstream Package Updates` 每 6 小时检查一次微信官方安装包版本,也支持手动触发
|
||||
2. 如果检测到版本号或安装包哈希变化,工作流会更新 `versions/upstream.env`
|
||||
3. 该文件变更提交到 `master` 后,会自动触发 `Build and Publish Docker Image`
|
||||
|
||||
版本状态文件位于 `versions/upstream.env`,当前记录了:
|
||||
|
||||
- 微信 amd64/arm64 下载地址
|
||||
- 微信 amd64/arm64 解析出的版本号
|
||||
- 微信 amd64/arm64 安装包 SHA256
|
||||
- 最近一次发生变更的检测时间
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **无法访问 Web UI**
|
||||
@@ -189,7 +269,7 @@ wechat-selkies/
|
||||
|
||||
查看容器运行日志:
|
||||
```bash
|
||||
docker-compose logs -f wechat-selkies
|
||||
docker compose logs -f wechat-selkies
|
||||
```
|
||||
|
||||
## 技术架构
|
||||
|
||||
117
README_en.md
117
README_en.md
@@ -22,6 +22,8 @@ This project packages the official WeChat/QQ Linux client in a Docker container,
|
||||
|
||||
> If some features are missing after an upgrade, please clear the `openbox` directory in the local mounted directory (e.g., `./config/.config/openbox`).
|
||||
|
||||
> This repository includes automatic upstream WeChat version detection: GitHub Actions periodically checks the official `.deb` packages, updates `versions/upstream.env` when changes are detected, and then triggers the image build workflow.
|
||||
|
||||
## Features
|
||||
|
||||
- 🌐 **Browser Access**: Use WeChat directly through web browsers without local installation
|
||||
@@ -33,6 +35,13 @@ 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)
|
||||
- 📂 **File Manager**: Built-in PCManFM lightweight file manager, accessible from the right-click menu for easy file management inside the container
|
||||
|
||||
## Screenshots
|
||||

|
||||

|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -47,18 +56,25 @@ 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
|
||||
```
|
||||
|
||||
> **Minimal image**: If you only need WeChat (without QQ and file manager), use the `minimal` tag for a smaller image:
|
||||
> ```bash
|
||||
> docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri ghcr.io/nickrunning/wechat-selkies:minimal
|
||||
> ```
|
||||
> Versioned minimal tags are also available, e.g. `:1.2.3-minimal`, `:1.2-minimal`, for pinning to a specific release.
|
||||
|
||||
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
|
||||
### Docker Compose Deployment
|
||||
1. **Create project directory and navigate into it**
|
||||
```bash
|
||||
mkdir wechat-selkies
|
||||
@@ -71,24 +87,43 @@ 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"
|
||||
- "${HTTP_PORT:-3000}:3000"
|
||||
- "${HTTPS_PORT:-3001}:3001"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/config
|
||||
devices:
|
||||
- /dev/dri:/dev/dri # optional, for hardware acceleration
|
||||
- /dev/dri:/dev/dri
|
||||
environment:
|
||||
- PUID=1000 # user ID
|
||||
- PGID=100 # group ID
|
||||
- TZ=Asia/Shanghai # timezone
|
||||
- LC_ALL=zh_CN.UTF-8 # locale
|
||||
- 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
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-100}
|
||||
- TZ=Asia/Shanghai
|
||||
- LC_ALL=zh_CN.UTF-8
|
||||
- AUTO_START_WECHAT=true
|
||||
- AUTO_START_QQ=false
|
||||
- CUSTOM_USER=${CUSTOM_USER:-}
|
||||
- PASSWORD=${PASSWORD:-}
|
||||
shm_size: "${SHM_SIZE:-1gb}"
|
||||
```
|
||||
3. **Start the service**
|
||||
3. **Create `.env` file (optional)**
|
||||
|
||||
Copy `.env.example` and modify as needed. Variables not set will use default values:
|
||||
```bash
|
||||
docker-compose up -d
|
||||
cp .env.example .env
|
||||
```
|
||||
`.env` file example:
|
||||
```env
|
||||
HTTP_PORT=3000
|
||||
HTTPS_PORT=3001
|
||||
PUID=1000
|
||||
PGID=100
|
||||
# CUSTOM_USER=
|
||||
# PASSWORD=
|
||||
SHM_SIZE=1gb
|
||||
```
|
||||
4. **Start the service**
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Source Code Deployment
|
||||
@@ -101,13 +136,18 @@ docker run -it -p 3001:3001 -v ./config:/config nickrunning/wechat-selkies:lates
|
||||
|
||||
2. **Start the service**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
3. **Access WeChat**
|
||||
|
||||
Open in browser: `https://localhost:3001` or `https://<server-ip>:3001`
|
||||
|
||||
> **Build minimal version**: When building from source, use build-arg to create a WeChat-only image:
|
||||
> ```bash
|
||||
> docker build --build-arg INSTALL_QQ=false --build-arg INSTALL_PCMANFM=false -t wechat-selkies:minimal .
|
||||
> ```
|
||||
|
||||
### Configuration
|
||||
|
||||
For more custom configurations, please refer to [Selkies Base Images from LinuxServer](https://github.com/linuxserver/docker-baseimage-selkies).
|
||||
@@ -124,7 +164,7 @@ This project supports pushing to both GitHub Container Registry and Docker Hub.
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
Configure the following environment variables in `docker-compose.yml`:
|
||||
Configure the following environment variables in `docker-compose.yml`. Variables with `${VAR:-default}` syntax can be overridden via a `.env` file:
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
@@ -148,6 +188,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
|
||||
@@ -164,6 +217,7 @@ devices:
|
||||
```
|
||||
wechat-selkies/
|
||||
├── docker-compose.yml # Docker Compose configuration file
|
||||
├── .env.example # Environment variables example file
|
||||
├── Dockerfile # Docker image build file
|
||||
├── LICENSE # License
|
||||
├── README.md # Project documentation (Chinese)
|
||||
@@ -177,6 +231,33 @@ wechat-selkies/
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Updating WeChat/QQ Version
|
||||
|
||||
When WeChat or QQ displays a "version outdated" message, simply pull the latest image and recreate the container. Your chat history and configurations will be preserved:
|
||||
|
||||
```bash
|
||||
# Using pre-built images
|
||||
docker compose pull && docker compose up -d
|
||||
|
||||
# Using source code build
|
||||
git pull && docker compose up -d --build
|
||||
```
|
||||
|
||||
> **Note:** The WeChat and QQ download URLs point to the latest official versions. Rebuilding the image will automatically download the newest version.
|
||||
|
||||
For maintainers, the current automation flow is:
|
||||
|
||||
1. `Detect Upstream Package Updates` checks the official WeChat packages every 6 hours and can also be triggered manually
|
||||
2. If the version or package hash changes, the workflow updates `versions/upstream.env`
|
||||
3. Once that file is committed to `master`, it automatically triggers `Build and Publish Docker Image`
|
||||
|
||||
The version state file is stored at `versions/upstream.env` and currently records:
|
||||
|
||||
- WeChat amd64/arm64 download URLs
|
||||
- Parsed WeChat amd64/arm64 package versions
|
||||
- WeChat amd64/arm64 package SHA256 hashes
|
||||
- The last detection time that actually changed the tracked state
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Unable to access Web UI**
|
||||
@@ -187,7 +268,7 @@ wechat-selkies/
|
||||
|
||||
View container runtime logs:
|
||||
```bash
|
||||
docker-compose logs -f wechat-selkies
|
||||
docker compose logs -f wechat-selkies
|
||||
```
|
||||
|
||||
## Technical Architecture
|
||||
|
||||
@@ -10,10 +10,12 @@ services:
|
||||
devices:
|
||||
- /dev/dri:/dev/dri # optional, for hardware acceleration
|
||||
ports:
|
||||
- "3001:3001"
|
||||
- "${HTTP_PORT:-3000}:3000" # http port
|
||||
- "${HTTPS_PORT:-3001}:3001" # https port
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=100
|
||||
# - CUSTOM_USER=<Your Name> # recommended to set a custom user name
|
||||
# - PASSWORD=<Your Password> # recommended to set a password for selkies web ui
|
||||
- PUID=${PUID:-1000} # set user id according to your system
|
||||
- PGID=${PGID:-100} # set group id according to your system
|
||||
- CUSTOM_USER=${CUSTOM_USER:-} # recommended to set a custom user name
|
||||
- PASSWORD=${PASSWORD:-} # recommended to set a password for selkies web ui
|
||||
shm_size: "${SHM_SIZE:-1gb}" # recommended, will improve performance
|
||||
|
||||
BIN
docs/images/wechat-selkies-1.jpg
Normal file
BIN
docs/images/wechat-selkies-1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/images/wechat-selkies-2.jpg
Normal file
BIN
docs/images/wechat-selkies-2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -1,8 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openbox_menu xmlns="http://openbox.org/3.4/menu">
|
||||
<menu id="root-menu" label="MENU">
|
||||
<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>
|
||||
<separator />
|
||||
<item label="File Manager" icon="/usr/share/pixmaps/xterm-color_48x48.xpm"><action name="Execute"><command>pcmanfm</command></action></item>
|
||||
<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>
|
||||
</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
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# clean up stale dbus pid file to prevent startup failures after container restart
|
||||
rm -f /run/dbus/pid
|
||||
|
||||
# configure openbox dock mode for stalonetray
|
||||
if [ ! -f /config/.config/openbox/rc.xml ] || grep -A20 "<dock>" /config/.config/openbox/rc.xml | grep -q "<noStrut>no</noStrut>"; then
|
||||
mkdir -p /config/.config/openbox
|
||||
@@ -8,11 +11,31 @@ 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
|
||||
# configure default window behavior: open WeChat/QQ as normal windows instead of maximized
|
||||
OB_RC="/config/.config/openbox/rc.xml"
|
||||
if [ -f "$OB_RC" ] && ! grep -q '<application class="wechat"' "$OB_RC"; then
|
||||
sed -i '/<\/openbox_config>/i \
|
||||
<applications>\
|
||||
<application class="wechat">\
|
||||
<maximized>no</maximized>\
|
||||
</application>\
|
||||
<application class="QQ">\
|
||||
<maximized>no</maximized>\
|
||||
</application>\
|
||||
</applications>' "$OB_RC"
|
||||
openbox --reconfigure 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 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 &
|
||||
|
||||
12
versions/upstream.env
Normal file
12
versions/upstream.env
Normal file
@@ -0,0 +1,12 @@
|
||||
# Upstream package state tracked by automation.
|
||||
|
||||
WECHAT_AMD64_URL="https://dldir1v6.qq.com/weixin/Universal/Linux/WeChatLinux_x86_64.deb"
|
||||
WECHAT_ARM64_URL="https://dldir1v6.qq.com/weixin/Universal/Linux/WeChatLinux_arm64.deb"
|
||||
|
||||
WECHAT_AMD64_VERSION="4.1.1.4"
|
||||
WECHAT_ARM64_VERSION="4.1.1.4"
|
||||
|
||||
WECHAT_AMD64_SHA256="ce6a5c201839383d6ab0198c026ecec274bd62803044aec61fdca20e02c797e2"
|
||||
WECHAT_ARM64_SHA256="5deb709bcb94bf368eb39d01934cc8456b3fd811d39b45e061c7348ddd8c05a4"
|
||||
|
||||
WECHAT_LAST_CHECKED_AT="2026-04-07T12:45:40Z"
|
||||
Reference in New Issue
Block a user