上一篇文章 里我写到了我尝试在 nspawn 里使用 deepin 来运行 qq 不过失败了,后来找到的原因发现只是 deepin-wine 在 kde 上使用有点问题,解决了以后凑合用了一会也没有再写出来,直到今天有人说也试着像我一样做了这件事,就干脆写一下吧。

诚如百合老师在博客里写的,由于腾讯(估计是)因为市场原因在数年前放弃了 QQ 的 Linux 版,并且在去年撤掉了本来就没多少东西剩下的 Web 版 QQ,使得在 Linux 上使用 QQ 日益困难重重,国内 Linux 用户最普遍的愿望之一大概就是彻底摆脱 QQ 或者是能有一个完善的方案了。同样国内大厂的其他产品例如微信、网盘等也都是类似的境遇,甚至连大家好心做的第三方客户端也遭到封杀,在这里我要先祝各位国内大厂产品经历死个马先。

毋须多言的一个问题是“为什么要用 QQ?”显然大家不是因为喜欢才要使用 QQ 的,比较起来 Telegram 在这些问题上好不止一点,然而 IM 的本质是与人交流,如果交流的对象用的是 QQ 你还跑的到哪去?

说起来在快十年前我们家买的新电脑(整机烈士)刚到手的时候打开是一个奇怪的系统,我至今也想不起来那究竟是哪一个 Linux 发行版,然后当时毫无基础甚至 Windows 也只是将将会用的情况下我摸爬滚打竟然硬是装上了 QQ 的 Linux 版,看着那跟 Windows 版比起来像是上世纪产品的 UI,我还是选择了重装 Windows(并没有因此而跟 Linux 结缘(笑))。

那么接下来就介绍一下在 9102 年迫不得已要在 Linux 上使用 QQ 的几种办法:

AppImage

askme765cs/Wine-QQ-TIM 提供的使用 AppImage 打包的 wine-QQ/wine-TIM 方案确实算得上是最简单的方案了,下载单个可执行文件后 chmod +x 再双击运行,只要你“运气够好”,也许就能从此不用再为这个问题劳神了。说“运气”,是因为 AppImage 确实将大部分的依赖环境都打包了进去,但是还是有用到本机的一些库,比如字体,又比如我在这个方案刚出来的时候就试用了一下完全没问题,结果放了一段时间想再用的时候就出来了奇奇怪怪的问题,而 AppImage 的封装使得这些问题更难寻找源头。

虚拟机

使用方法略,考虑到只为了一个 QQ 这样的不算大的程序而运行一整个 Windows 也太浪费了。

直接使用 Wine

在这个方案上做的最好的肯定是唯一拿得出手的国产操作系统——Deepin 了。不提 Deepin 作为一个发行版在其他方面做的如何,从一开始免费提供 Crossover 授权到后面自己开发 deepin-wine,以及协助大厂开发的 WPS 和网易云音乐 Linux 版,可以看的出 Deepin 确实是在为国内用户费了不少功夫。在其他的操作系统上当然也可以用 deepin-wine,只不过需要注意的一点是由于 deepin-wine 用到了 gnome-settings-daemon 的一些 api,在非 Gnome 系 DE(例如 KDE)需要额外运行一个 gnome-settings-daemon。

在 ArchLinux 上可以直接用 archlinuxcn 源提供的 deepin-qq-eim, deepin.com.qq.im 等包,然后运行 /usr/lib/gsd-xsettings. 或者,你也可以选择用普通的 wine 来运行,这样尽管运行时体验差一点(卡,不稳定),但好歹在 KDE 上使用时清真一点,可以选择百合老师做好的环境,或者是其他人的方案,相关的资料很多(质量也非常参差不齐),这里不再多说了。

这个方案有两个不清真的地方,一是 wine 会把一大坨的 32 位库塞进你的系统,在运行时还会制造许多的垃圾(可以通过 prefix 缓解),二是在 Arch 这样更新勤快的滚动发行版上,wine 的每一次更新都可能使之前好不容易做好的环境出现运行不稳定的情况,在以前数次惨痛的浪费人生经历后,我决定再也不在本机碰完成度低于 Steam Play 的 wine 方案了。

在容器中使用 wine

上一段提到我不想让 wine 弄脏我的本机环境,那么相应地把 wine 丢进容器中使用听起来就可以接受了,毕竟眼不见心不烦嘛。比较常见的容器是 docker,现在也有很多人写好了 qq 微信相应的 dockerfile. 但是当你仔细看一下他们的 dockerfile,就会发现他们也都是塞了一整个 rootfs 进了 docker,甚至还有人用 ubuntu 的 rootfs 装 deepin-wine 的包,这使得本身也称不上“清真”的 docker 变成了跟直接使用 wine 好不了多少的“不清真”。另外,在我上一篇文章中提到,我已经开启了 Cgroup V2,因此反正也用不了 docker。与之相对的,我们使用 systemd-nspawn 来完成这个任务。

首先我们要做一个 rootfs,如果你用的是 Gnome 系桌面的话,建议直接使用 Deepin 做 rootfs 来使用全套的 deepin-wine 方案,而如果是其他的比如 KDE 的话,则在这里示范用 Arch 做 rootfs 并使用普通的 wine。

# 开放 xhost 权限
$ xhost +
# 先用 deepin
$ mkdir deepin
# 用 debootstrap 构建 rootfs
$ sudo debootstrap --include=systemd-container unstable deepin http://mirrors.ustc.edu.cn/deepin/ 
# 由于 debian 系默认不允许无密码 root 登录,因此先设个密码。不加 -b 的话就类似于
$ sudo systemd-nspawn -D deepin
root@deepin:$ passwd
# 还有创建你的普通用户和对应的家目录之类的,就不写了
# 然后退出来再用一次完整 systemd-nspawn. 其中 -b 将使得容器像一个完整的系统启动,包括全套 systemd 管理的 init 服务
# 其他几个 bind mount 参数使得我们可以在容器中运行 GUI 软件并有声音
$ sudo systemd-nspawn --bind=/tmp/.X11-unix:/tmp/.X11-unix --bind=/run/user/1000/pulse:/run/user/host/pulse --setenv=LANGUAGE=zh_CN:zh -bD deepin
# 改源并安装几个要用的东西
root@deepin:$ dpkg --add-architecture i386
root@deepin:$ echo "deb http://mirrors.ustc.edu.cn/deepin unstable main contrib non-free" > /etc/apt/sources.list
root@deepin:$ apt update
root@deepin:$ apt install deepin.com.qq.im.light
# 然后切换到普通用户
root@deepin:$ su kiri
# 重要!设置环境变量。变量值具体等于多少去看你自己本机的变量。
# 如果还需要声音就加个 PULSE_SERVER=unix:/run/user/host/pulse/native
kiri@deepin:$ export DISPLAY=:0
# 然后就直接试着运行
kiri@deepin:$ /opt/deepinwine/apps/Deepin-TIM/run.sh

这样就能算得上能用了,剩余的步骤跟接下来要讲的用 Arch rootfs 一样:

$ xhost +
$ mkdir march
# 用 pacstrap 生成 rootfs,当然你也可以用 nspawn.org 做好的 image
$ sudo pacstrap -i march base
# arch 就没有 root 必须有密码的要求了
$ sudo systemd-nspawn --bind=/tmp/.X11-unix:/tmp/.X11-unix --bind=/run/user/1000/pulse:/run/user/host/pulse --setenv=LANGUAGE=zh_CN:zh -bD march
# 然后自己设一下源之类的东西,arch 用户就不用我多说了。后面几个库是用于声音之类的。
$ pacman -S wine lib32-gnutls lib32-alsa-plugins lib32-libldap lib32-libpulse
# 然后!把百合老师的 qq 轻聊版环境丢到 /opt 下面
# 一样的自己创建好普通用户,然后设置环境变量
kiri@march:$ export DISPLAY=:0
# 试试看直接运行
kiri@march:$ /opt/QQLite/qq

到这里我们已经可以确定这个方案可行了,但是距离日常用的方便还有一定距离。我们试着用 machinectl 来管理容器,并用 systemctl 来管理 QQ 进程:

# machinectl 的话必须把容器放到 /var/lib/machines/ 下面,讲道理这是有点烦人的
$ sudo mount --bind march /var/lib/machines/march
# 但是现在不要直接用 machinectl 启动!不然你的文件权限会因为 nspawn 的机制而变得一团乱,但我们的目的很简单没有什么安全要求,所以就只留下自己需要的设置
# 容器的配置要放在 /etc/systemd/nspawn 下的 .nspawn 文件里
$ cat /etc/systemd/nspawn/march.nspawn
[Exec]
PrivateUsers=no # 这一行就是上面说的

[Files]
BindReadOnly=/tmp/.X11-unix:/tmp/.X11-unix
Bind=/run/user/1000/pulse:/run/user/host/pulse
Bind=/dev/shm:/dev/shm
Bind=/home/kiri/Documents:/home/kiri/Documents # 这一行是让宿主机和容器共享一些资料文件夹,这样我用 qq 下的一些文件就可以方便地读取了

[Network]
Private=no

# 然后再启动容器
$ sudo machinectl start march
# 以后要登录容器的 shell 可以用 login 或者 shell
$ sudo machinectl shell march
# 编写一个启动 qq 用的服务
root@march:$ cat /etc/systemd/system/qq.service
[Unit]
Description=qq

[Service]
Type=forking
User=kiri
Environment=DISPLAY=:0
Environment=GTK_IM_MODULE=fcitx
Environment=QT_IM_MODULE=fcitx
Environment=XMODIFIERS=@im=fcitx
ExecStart=/opt/QQLite/qq
#KillMode=process
Restart=on-abnormal
RestartSec=2
#MemoryHigh=512M
MemoryMax=512M
MemorySwapMax=128M

[Install]
WantedBy=multi-user.target

# 要不要 enable 来开机启动就随便你了

在一切准备以后,你还可以用 machinectl pull-tar 做一下备份,以免下一次出现玄学问题可以退回完美的版本。作为 zfs 用户的话就可以美滋滋地用 zfs snapshot 和 zfs rollback 啦~

用 EFB 来转发 QQ 消息到 Telegram

ehForwarderBot 是连接多个 IM 的桥梁,目标就是让你可以只用一个 IM 来收发其他 IM 的消息。在之前 EFB 还只能转发微信(我也在用着),而在前不久在奶冰的努力下也能转发QQ了,原理就是通过在服务器上运行 CoolQ 并跟 Telegram 的 bot 对接。

这个做法当然可以满足基本需求,但是由于 QQ 的功能比微信要多太多了,诸如群投票之类乱七八糟的东西,尽管我还没用 EFB 试过,但想来是不会很好的;又比如我打开 qq 常干的事之一是翻群历史文件,这显然也不简单。

但总之不失为一种好方法吧,有需要可以自己尝试。

其他杂谈

以前 Rikka 还有个项目叫 FCM-for-Mojo, 利用的是 Web QQ 来实现消息推送,上游是 Mojo-Webqq, 姑且也能算是 QQ 的 Linux 客户端吧,然后随着 Web QQ 的消失这俩倾注心血维护的项目都变得毫无意义了(Web QQ 就算快倒闭了也时不时改 api,搞得下游项目得频繁更新才能用),万恶的资本主义企业啊。

本来的话 Anbox 也有让 Android 版 QQ 在 Linux 上使用的希望,然而这个中看不中用的开源拖拉机(无恶意)几年过去了还是没什么起色,基本上就没啥 app 能用的 qwq。

还有一些没啥用的奇技淫巧,比如用 scrcpy 把你的 Android 设备映射到屏幕上来操作。

总之本文就先到这里啦,接下来半年写文章的频率只会越来越低了,恩。