分類
Linux 软件

小內存 OpenWrt 折騰記錄

我有一個路由器(CPU:580 MHz, RAM: 128 MB, Flash: 16 MB),幾年前刷過 OpenWrt 19,三年來工作的很好,裡面的 dnscrypt2 也運行的很好。最近看到可以更新到 23.05.4,於是打算試試。順便把一個舊優盤的容量擴展上去試試更多的軟件。

重裝 OpenWrt

由於我是跨版本升級,所以為了避免出現問題選擇的不保留資料。在 Table of Hardware 查詢自己的路由器,然後在 Device page 就能找到升級所需要的 Sysupgrade image。 也可以到 Download OpenWrt firmware for your device 搜索並下載所需的升級鏡像。而且這個下載頁還有一個 Customize installed packages and/or first boot script,裡面可以添加預裝軟件。有興趣的朋友可以嘗試將 wget-ssl 添加進去。下載 squashfs-sysupgrade.bin 文件到電腦後,就可以路由器界面 System > Backup / Flash Firmware > Flash new firmware image 升級系統了。升級後使用網線連接電腦就能獲得 IP。瀏覽器輸入 192.168.1.1 就能進入控制台。進入後設置密碼和密鑰,再到 Network > Wireless 開啟 WiFi,路由器就可以使用了。

為 opkg 配置代理

如果你網絡沒問題,就不必使用代理,但國情在此,直連的話基本上無法更新軟件。正常情況下設置代理非常簡單:

#進入路由器
ssh [email protected]
#添加代理到到 /etc/profile
echo "export http_proxy=http://192.168.1.235:44083" >> /etc/profile
echo "export https_proxy=http://192.168.1.235:44083" >> /etc/profile
#重新加載 profile
source /etc/profile
echo $http_proxy
#此時應出現 http://192.168.1.235:44083/
wget http://ip-api.com/json
#應能成功顯示 proxy 的 IP

但是使用 wget 下載 https 連接卻會出現 400 錯誤,可以先把軟件源替換成 http 安裝 wget-ssl 後再替換回來。

#備份下軟件源
cp /etc/opkg/distfeeds.conf distfeeds.conf
#然後將軟件源中的 https 替換為 http
sed -i -e "s/https/http/" /etc/opkg/distfeeds.conf
#之後就可以更新並安裝軟件了
opkg update
opkg install wget-ssl
#安裝 wget-ssl 後就可以換回 https 的軟件源了
cp distfeeds.conf /etc/opkg/distfeeds.conf
#在 opkg 配置文件李設置代理
#/etc/opkg.conf 中添加
option http_proxy http://192.168.1.1:44083/
option https_proxy http://192.168.1.1:44083/
#後台運行程序
opkg install coreutils-nohup

使用優盤為路由器拓展空間

不到 10 MB 的可用空間非常限制 OpenWrt 的可玩性,好在可以用優盤來拓展。首先在電腦上把優盤格式化成 ext4 格式:

#查看優盤盤符
lsblk
#得到類似 /dev/sdb 或 /dev/sdc

#格式化成 ext4 格式
sudo mkfs.ext4 /dev/sdX

然後將優盤插入路由器:

#安裝依賴
opkg update
opkg install block-mount kmod-usb-storage kmod-fs-ext4 e2fsprogs

#建立掛載點
mkdir -p /mnt/usb
#查看優盤盤符
ls /dev/sd*
#得到類似 /dev/sda
#掛載優盤
mount /dev/sda /mnt/usb
#查看掛載結果
df -h
#應該可以看到 /dev/sda 掛載到了 /mnt/usb

#備份 /overlay
cp -a /overlay/* /mnt/usb
#卸載優盤
umount /mnt/usb
#掛載到 /overlay
mount /dev/sda /overlay
#更新 /etc/config/fstab,在文件底部增加如下內容
config 'mount'
   option  target  '/overlay'
   option  device  '/dev/sda'
   option  fstype  'ext4'
   option  options 'rw,sync'
   option  enabled '1'
   option  enabled_fsck '0'
#重啟路由器即可看到路由器顯示優盤容量
reboot

其他 OpenWrt 命令行小撇步

在使用 scp 從電腦往路由器傳文件時出現 ash: /usr/libexec/sftp-server: not found,可以通過添加 -O 參數來解決:

scp -O source target
#如果是文件夾或多個文件
scp -O -r source target

在命令行下載文件文件時,鏈接過長導致無法輸入完整鏈接?可以安裝 bash

opkg install bash
#使用 bash 而不是 ash
bash
wget https://a.vrey.long/url/that/you/would/like/to/access
#返回 ash
exit

嘗試在 OpenWrt 上編譯 Python 庫

測試的項目是 ssr-command-client,這裡只能說歷經劫難修成正果,最終編譯出來了也成功安裝上了。但是由於路由器性能實在孱弱,並不能直接運行 ssr-command-client,不過可以通過自己寫一個 Python 腳本,只引用需要的庫來運行。

使用更輕便的 HTTPS DNS Proxy 來增加網絡安全性

安裝 https-dns-proxyluci-app-https-dns-proxy 然後重新登錄路由器,頂部導航應該就出現 Services > HTTPS DNS Proxy 了。在 HTTPS DNS Proxy - Instances 中編輯或添加 DNS 服務器。

Provider: Custom
Parameter: https://YOUR.DNS.PROVICDER/PATH
Bootstrap DNS:
Listen Address: 127.0.0.1
Listen Port: 5353
Run As User: nobody
Run As Group: nogroup

經過兩周的使用,發現這個路由器還是可以在性能有限的情況下同時穩定運行 ssr + v2fly + dnscrypt2 的。最高網速相較於電腦上的客戶端可能有所下降,但是也還好。

本文更新於 2024/11/08。

分類
Linux 软件

使用 nginx 代理 NextDNS 的 DoH 服務

免費版的 NextDNS 每個月有 300,000 次請求,個人一般是夠用的。通過 nginx 代理其 DoH 服務可以用自己的域名來使用 NextDNS 的服務。Nginx 配置文件 /etc/nginx/conf.d/MY.DOMAIN.conf 如下:

# Cache the dns query
proxy_cache_path /var/cache/nginx/dns levels=1:2 keys_zone=dns_cache:10m max_size=1g inactive=1d use_temp_path=off;

server {
    server_name my.domain;

    root /usr/share/nginx/html;

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/my.domain/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/my.domain/privkey.pem; # managed <p>by</p> Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    http2 on;
    resolver 45.90.28.136;

    location /YOUR_DNS_QUERY_PATH {
        proxy_pass https://dns.nextdns.io/YOUR_ID/YOUR_TAG;
        proxy_set_header Host dns.nextdns.io;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache dns_cache;
        proxy_cache_valid 1d;
        proxy_cache_use_stale error timeout updating;
    }
    
    location / {
    }
}
server {
    if ($host = my.domain) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;
    server_name my.domain;
    return 404; # managed by Certbot


}

Nginx 搭配 Cerbot 獲取免費證書請參考:網站用上了letsencrypt的免費ssl證書

搭建完成後可以通過 curl 來檢查服務是否正常:curl -H 'accept: application/dns-json' 'https://my.domain/YOUR_DNS_QUERY_PATH?name=ft.shaman.eu.org&type=A'

使用 personalDNSfilter 以在安卓手機上使用 DoH

使用 personalDNSfilter (去 F-droid 下載)實現的本地 VPN 來修改系統的DNS查詢,它同時還能攔截廣告。配置非常簡單,只需要在 DNS 配置欄選中禁用 DNS 服務器查找,選中文本編輯模式,並在輸入框中加入下面一行並啟用即可。

[MY.SERVER.IP]::443::DOH::https://my.domain/YOUR_DNS_QUERY_PATH

也可是使用重一點的 Rethink 來管理 DNS。加入自己的 DoH 也很簡單。選擇 Other DNS,DoH,新增。名稱隨便,Resolver URL 填:https://my.domain/YOUR_DNS_QUERY_PATH 即可。

在 Xfce 口味的 Fedora 上使用 DoH

似乎是沒辦法直接在圖形界面上進行設置。我最終是用熟悉的 dnscrypt-proxy 2 來代理。不熟悉的朋友請參考:Fedora 使用 DNSCrypt。只需要修改三處文件即可:

#file:dnscrypt-proxy.toml

#設置 server_names,默認是注釋掉的
server_names = ['mydns']

#如果在中國,可以把 bootstrap_resolvers 設置成 114
bootstrap_resolvers  = ['114.114.114.114:53', '8.8.8.8:53']

#拉到文件底部,添加這兩行
  [static.mydns]
  stamp = 'sdns://AgAAAAAAAAAABzEuMS4xLjEACW15LmRvbWFpbhQvWU9VUl9ETlNfUVVFUllfUEFUSA'

stamp 的生成請打開這個網頁:Online DNS Stamp calculator Protocol 選擇 DoH,IP 寫 IP,Host 寫域名,Path 寫/YOUR_DNS_QUERY_PATH ,然後複製生成的 Stamp 就可以了。

今天是 2024 年 4 月 1 日,看看自己域名的 DoH 可以存活多久。安卓網絡設置里雖然可以直接指定 DoT,NextDNS 的 DoT 也可以用 Nginx 代理,但是感覺陣亡幾率更大(專用端口),所以我選擇了更穩妥一點的 DoH。

2024-04-05: 移動網絡偶爾出現 SSL handshake timed out IO Error occured! Check network or DNS config! 和 Read timed out IO Error occured! Check network or DNS config! 但是還是能用。

2024-04-06: 晚上移動網絡開始出現 SSL handshake aborted: ssl=0x7b2db05fc8: I/O error during system call, Connection reset by peer IO Error occured! Check network or DNS config! 基本用不了了。之間訪問網站也顯示 The connection was reset 。所以清明節沒過完域名就掛了,存活時間約 5 天。如果想要繼續使用被屏蔽的域名,或者直接使用 NextDNS,無論是 DNSCrypt 還是 personalDNSfilter 都支持代理。一息尚存的 DNSCrypt 內置不少 DNS,還可以在不用代理的情況下使用。

2024-04-15:偶爾外出就餐時連接餐廳的 WiFi,發現電信的寬帶仍然可以使用。但是移動網絡和移動寬帶已經完全無法連上。


另請參閱:使用 Python 或/和 Cloudflare Worker 代理 NextDNS 的 DoH 服務

本文更新於 2024/04/24。

分類
Linux

Fedora 39 Xfce tricks

前段時間用了一些 KDE 團隊開發的應用,又聽說 Linux 界的安卓模擬器新星 Waydroid 很厲害,於是裝個 KDE Plasma 體驗。結果離開了 X 才只知道 X 的好!KDE Plasma 在默認配置下,瀏覽器播放視頻會有輕微卡頓,如果快進快退則卡頓感更加明顯。而且原來可以輕鬆設定的全局熱建,搜索一通居然發現無法實現!在英文系統里裝中文輸入法,也是個痛。所以用了一天時間就跟這酷炫的桌面拜拜了——裝回了順手的 Xfce。

多媒體工作站

	
sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm

sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
sudo sh -c 'echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/yum.repos.d/vscode.repo'

dnf check-update
sudo dnf install code vlc ffmpeg libreoffice calibre

安裝拼音輸入法

Applications > Settings > Input Method Selector,選擇 Use IBus。然後點擊旁邊的 Preferences 打開 IBus 的設置。

在 General 標籤頁可以將候選詞設置為橫排(默認為豎排)。在 Input Method 標籤頁點擊 Add 按鈕添加輸入法(選擇 Chinese > Intelligent Pinyin)。添加後選中拼音輸入法,在點擊右邊的 Preferences 可以設定拼音輸入法,比如候選詞數量、候選詞翻頁按鍵、字典詞庫以及中英文切換鍵等。設置過程中如果有問題,可能是需要登出一下才會生效。

調大字體

歲月不饒人,雖然屏幕是 1920*1080 全高清,但是字體顯示真的有點小,看久了會累。在 Applications > Settings > Apperance 中的 Fonts 標籤頁可以設置 DPI,改為 119,字體大小就舒服多了。Style 標籤也有很多主題,但是丑的居多,個人覺得默認的 Adwaita 確實已經是最好看的了。在 Applications > Settings > Window Manager 中也可以設定窗口樣式,推薦大家試試 Mint-Y-Aqua。

睡眠模式掉電快

在我的電腦上,默認的睡眠模式是 s2idle,如果想要在睡眠時省電,需要改成 deep 模式。

#查看當前模式
cat /sys/power/mem_sleep
#可能會輸出 [s2idle] 或[s2idle] deep
#通過下面命令設定成 deep 模式
echo deep | sudo tee /sys/power/mem_sleep
#再次查看 /sys/power/mem_sleep
#應該變成 s2idle [deep]
#此時可以嘗試睡眠觀察一下電量消耗
#如果沒有問題,還需要通過下面命令將這個模式保存下來

#打開 /etc/default/grub 文件
sudo nano /etc/default/grub
#找到 GRUB_CMDLINE_LINUX= 開頭的一行
#有的系統是 GRUB_CMDLINE_LINUX_DEFAULT= 但是沒關係
#這行中一般都還有一個關鍵字 quiet
#在這行的末尾,引號前加入
 mem_sleep_default=deep
#然後保存文件
#最後執行下更新啟動項
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

Virtual Box

使用sudo dnf install VirtualBox virtualbox-guest-additions即可安裝 VirtualBox,現在最新版是 7.0 。安裝後進入虛擬機發現共享文件以及顯示縮放都不能用。原來是自動下載失敗了,需要手動下載虛擬機增強插件 ISO。從 https://download.virtualbox.org/virtualbox/7.0.14/VBoxGuestAdditions_7.0.14.iso 下載後,掛載上就可以在虛擬機里安裝增強插件了。


其他小撇步另請參考:Fedora Workstation Tricks

本文更新於 2024/03/16。

分類
Linux 软件

Turmux X11 以及 VSCode

本篇文件介紹如何在安卓手機上運行 VSCode 來實現在旅途中繼續編碼。我使用的手機是 2018 年的 Xperia XZ2,內存只有 4GB,CPU 型號是 Qualcomm SDM845,屏幕是 5.7 in (140 mm) 1080p (2160 × 1080) IPS LCD, ~424 pixel density。最終效果如下:

VSCode On Debian With Termux

藉助 Termux 我們得以在安卓上運行多種 Linux,之所以選擇 Debian 是因爲它相對輕量且不含 systemd (安卓不支持)。本文得以形成主要是參考了 Termux X11:手機的X伺服器使用教學Android手機安裝Linux發行版:Termux proot-distro使用教學 以及 VSCode installation tutorial with Termux-x11,在此一併感謝!

如果你的手機性能良好,推薦按照 Ivon 的文章安裝 Xfce4 桌面環境,這樣東西就一應俱全了(除了不能運行 Docker,安卓內核不支持)。

安裝 Termux

雖然可以直接去 Github 下載安裝包,但是還是建議去 F-Droid 安裝Termux。當前的最新版本是 0.118.0。安裝完成後執行下列命令:

pkg update
pkg upgrade
pkg install nano proot-distro x11-repo termux-x11-nightly
termux-setup-storage

安裝 X11

這裏需要到 Github 下載 X11 的安裝包。如果沒有 Github 賬戶,可以從這裏下載我安裝的這個版本。

安裝 Debian

手機性能好的話,可以嘗試其他口味的 Linux 以及桌面環境。我的手機連 Xfce4 也運行不起來(X11 裏可以,但是 Debian 裏就會自動退出),所以我使用輕量的 openbox。

proot-distro install debian
#進入 Debian
proot-distro login debian
apt update
apt upgrade
apt install nano sudo wget git
#添加普通用戶
passwd
groupadd storage
groupadd wheel
groupadd video
#替換 [USER] 爲你想要的名字
useradd -m -g users -G wheel,audio,video,storage -s /bin/bash [USER]
passwd [USER]
visudo
#在 root ALL=(ALL:ALL) ALL 這行的後面添加
[USER] root ALL=(ALL:ALL) ALL
#保存後使用 exit 退出 Debian

創建兩個腳本來啓動和關閉 X11:

##以下是startDebian.sh
#!/bin/bash
export DISPLAY=:0
killall -9 termux-x11 termux-wake-lock
# 啟動Termux X11
am start --user 0 -n com.termux.x11/com.termux.x11.MainActivity
XDG_RUNTIME_DIR=${TMPDIR}
termux-x11 :0 &
sleep 3

proot-distro login debian --user [USER] --shared-tmp
##以下是stopDebian.sh
#!/bin/bash
killall -9 termux-x11

然後給予可執行權限:

chmod +x startDebian.sh
chmod +x stopDebian.sh
#然後執行 startDebian.sh 就能就如 Debian 了
./startDebian.sh
#若出現報錯 ERROR: openbox-xdg-autostart requires PyXDG to be installed 可以按回車鍵忽略
#安裝窗口管理器
sudo apt install xorg openbox
sudo apt install firefox-esr fonts-noto-cjk proxychains4 curl tint2 xclip pcmanfm
#火狐:你需要一個瀏覽器
#fonts-noto-cjk:字體
#proxychains4:沒有代理不科學
#curl:安裝 curl 可以修復使用 proxychains4 時 library "libdl.so.2" not found 的錯誤
#tint2:任務欄讓桌面更美好
#pcmanfm:桌面環境怎能少得了文件管理器
#xclip:把剪切版的內容從安卓傳遞到 Debian

# .bashrc 中添加下面文本

alias pp='termux-clipboard-get | xclip -sel clipboard'
alias p='proxychains4'

export DISPLAY=:0
if ! pgrep -x "openbox" > /dev/null; then
    openbox-session &
    tint2 &
fi

安裝 VSCode

前往 VSCode 下載頁,下載 Arm64 版本的 .deb 安裝包。可以通過瀏覽器下載,然後下載頁會有下載的直鏈,複製後用 wget 下載就好。

#安裝 VSCode
sudo dpkg -i /sdcard/Downloads/code-[VERSION].deb
#如果出現報錯嘗試
sudo apt --fix-broken install
#然後再試試上面的 dpkg 安裝指令

最在啓動欄創建兩個常用的快捷啓動方式:先修改下桌面快捷方式

#把 /usr/share/applications/code.desktop 中的
Exec=/usr/share/code/code --unity-launch %F
#改爲
Exec=/usr/share/code/code --no-sandbox %F

#把 /usr/share/applications/firefox-esr.desktop 中的
Exec=/usr/lib/firefox-esr/firefox-esr %u
#改爲
Exec=/usr/lib/firefox-esr/firefox-esr --no-sandbox %u

兩個手機點一下桌面(黑屏)呼出右鍵菜單,選擇 Applications > Settings > Tint2 Settings 。點擊左上角編輯,Panel 推薦設置在左邊,length 150%,size 60 pixels。Panel items 留下 Launcher 和 Taskbar 即可。Lanucher 裏選擇 VS Code 和 Firefox ESR 保存即可。

既然提到了命令行啓動火狐,順便記錄下火狐的多用戶。在命令行加上 -P 可以啓動 Profile 管理器。在管理器裏可以新增、重命名以及設置默認的用戶。想以用戶 42 啓動,則把上面快捷方式裏的運行項改爲 /usr/lib/firefox-esr/firefox-esr --no-sandbox -P 42 %u 即可。

Firefox 在 A Profile 升級後,从 B Profile 啟動可能會出現問題:"You've launched an older version of firefox" "Using an old version of Firefox..."。此時可以在啟動命令後加上 -allow-downgrade 就可以啟動了。然後關閉火狐就可以正常啟動了。

使用場景

開始使用

  1. 啓動 Termux
  2. ./startDebian.sh

退出

  1. 切換到 Termux,執行 exit
  2. ./stopDebian.sh

其他小撇步

字體非常小:在 Debian 家目錄新增文件 nano ~/.Xdefaults 內容爲 Xft.dpi: 192 即指定 dpi 分辨率爲 192。如果個別應用字體還是很小,可以嘗試通過應用自己的配置來調整顯示大小。顏色深度也可以在這裏配置,例如:Xft.depth: 16

剪切版:從 Debian 到安卓默認是沒問題的,除非你在 X11 的配置項裏禁用了。但是從安卓到 Debian 卻不輕鬆。目前一個可行的方案就是上面使用 xclip 的方式:首先切換到 Termux,運行 alias pp,然後就可以在 Debian 裏粘貼了。

中文輸入:本篇沒有安裝中文輸入法,以節省更多資源予 VSCode。

掛載外部 SD Card 到 Debian:啓動時加入 --bind /data/data/com.termux/files/home/storage/external-1:/sdcard1 就會將外部儲存卡的可用目錄掛載到 proot 系統裏的 /sdcard1。手機裏的內部儲存默認是掛載在 /sdcard 的。

本文更新於 2024/03/03。

分類
Linux

使用 xinput 映射鼠標按鍵

鼠標左鍵故障無法使用了,可以使用 xinput 將其他不常用的按鍵映射爲左鍵。Fedora 的 Xfce 桌面環境沒有默認安裝 xinput,需要手動安裝一下。

sudo dnf install xinput
#查看設備列表
xinput list
#比如我的鼠標會找到
Bluetooth Mouse M336/M337/M535 Mouse    	id=20	[slave  pointer  (2)]
#後續操作使用 20 和 Bluetooth Mouse M336/M337/M535 Mouse 都可以
#查看此鼠標當前的按鍵映射配置
xinput --get-button-map 'Bluetooth Mouse M336/M337/M535 Mouse'
1 2 3 4 5 6 7 8 9 10 11 12
#查看當前鼠標按鍵
xinput test 'Bluetooth Mouse M336/M337/M535 Mouse'
#當你操作鼠標時機會顯示哪個按鍵有動作
#通過映射拯救左鍵
xinput set-button-map 'Bluetooth Mouse M336/M337/M535 Mouse' 1 1 3 4 5 1 1 1 1 1 1 1
#我把不常用的鍵都指定成左鍵了
#測試沒有問題後可以將映射的指令添加到開機啓動裏
#Applications > Settings > Session and Startup > Application Autostart
/usr/bin/xinput set-button-map 'Bluetooth Mouse M336/M337/M535 Mouse' 1 1 3 4 5 1 1 1 1 1 1 1

用了一小段時間後發現,似乎鼠標滾輪也不是很好,所以最終在屋裏找到另一隻鼠標後,放棄了這隻。建議大家旅行時攜帶鼠標最好有個盒子,免得擠壓導致鼠標死亡。

分類
程序

在 CentOS 8 上使用 Xvfb 和 PyAutoGUI

本文介紹在 CentOS 8 上使用 PyAutoGUI 操作運行與 Xvfb 桌面環境中的 Firefox 所需的準備。由於所操作網頁需要登錄並有 OTP,所以還需要使用 VNC 來手動輸入密碼以做準備工作。

安裝 Xvfb

sudo yum install xorg-x11-server-Xvfb
#啓動 Xvfb
Xvfb :0 -screen 0 1366x768x24+32 -br +bs -ac &
#修改環境變量 DISPLAY
export DISPLAY=:0
#查看環境變量 DISPLAY
$DISPLAY
#查看 Xvfb 進程
ps -ef | grep Xvfb

如果只是爲了使用 webdriver 操作瀏覽器那麼這樣安裝運行 Xvfb 後就可以了,但是我們要使用 PyAutoGUI,所以還有依賴要裝。

安裝火狐瀏覽器

我個人比較偏好 ESR 版火狐,下載後解壓就可以使用了。

tar -xf firefox-115.5.0esr.tar.bz2
cd Firefox
./firefox
#如果要指定窗口大小可以
./firefox -width 1350 -height 764

安裝 x11vnc 服務

其實只要下載執行檔,運行即可,非常簡單。不過 x11vnc 12 年未有更新,不知道還能用多久。如果那天不能用了可以嘗試 Tiger VNC。

mv x11vnc-0.9.13_amd64-Linux x11vnc
chmod +x x11vnc
#運行 vnc server,密碼設置長一點,端口是 9999
./x11vnc -display :0 -ncache 0 -passwd piHrcHxmJauvIOftenUseA64digitPasswordpokiHJHQWdsgcGFTG -rfbport 9999
#上面命令在一次連接後會停止,如果要服務一直可以用,就加上 forever 參數
./x11vnc -display :0 -ncache 0 -passwd piHrcHxmJauvIOftenUseA64digitPasswordpokiHJHQWdsgcGFTG -rfbport 9999 -shared -forever
#注意服務器的網絡防火牆要打開 9999 端口
#用 nmap 查看端口是否打開
nmap -p 9999 1.1.1.1

Fedora 本地可以安裝 Remmina 作爲 VNC 客戶端。

sudo dnf install remmina -y
#如果想要使用 socks5 代理
proxychains4 /usr/bin/remmina

打開 Remmina 後在地址欄的協議選項裏選擇 VNC,然後地址填 1.1.1.1:9999 回車就可以連上服務器的桌面並看到剛剛打開的火狐瀏覽器進行設置了。點左上角的 + 號可以添加 profile,這樣下次雙擊就能連上 VNC 了。

安裝與配置 PyAutoGUI

我自己從源碼編譯的 Python 一直提示沒有 _tkinter 模塊。搜了半天沒有解決,於是使用系統自帶的 Python 3.9 得以解決。雖然我是用 Python 3.11 開發的,但是 Python 3.9 跑起來也沒問題。

import _tkinter # If this fails your Python may not be configured for Tk
ImportError: No module named _tkinter
#安裝 Python 3.9 以及依賴
sudo yum install libnsl ImageMagick xclip
sudo yum install python39 python39-tkinter

#xdotool 並不支持 Xvfb,所以不用裝
#sudo yum install xdotool

#創建虛擬環境
cd your_project
python3.9 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

示例代碼

import json
import random
import sys
import requests
import time
import logging
import os
import pyperclip
import subprocess
from urllib.parse import urlparse, parse_qs, urlunparse
from dotenv import load_dotenv
import psutil
import pyautogui
import pyscreeze

script_dir = os.path.dirname(os.path.realpath(__file__))

load_dotenv()
api_key = os.getenv("API_KEY")
proxy = os.getenv("PROXY")
de = os.getenv("DE")
batch = int(os.getenv("BATCH"))

if proxy is None:
    proxies = None
else:
    proxies = {"http": proxy, "https": proxy}

logging.basicConfig(
    level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger()
# Create a file handler to save logs to a file
log_file = os.path.join(script_dir, "logfile.log")
file_handler = logging.FileHandler(log_file)

# Set the logging level for the file handler (if different from the root logger)
file_handler.setLevel(logging.INFO)  # Adjust the log level if needed

# Create a formatter and add it to the file handler
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
file_handler.setFormatter(formatter)

# Add the file handler to the logger
logger.addHandler(file_handler)


def activate_window(window_title):
    if de == "Xvfb":
        firefox_exists = False
        for proc in psutil.process_iter():
            try:
                if "firefox" in proc.name().lower():
                    firefox_exists = True
                    break
            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                pass
        if firefox_exists is False:
            subprocess.Popen(
                [
                    "/home/centos/fred/programs/firefox/firefox",
                    "-width",
                    "1350",
                    "-height",
                    "764",
                ]
            )
            logger.info("firefox started")
            time.sleep(10)
    elif de == "Xfce":
        command = f"xdotool search --onlyvisible --name '{window_title}' windowactivate"
        subprocess.run(command, shell=True)


def wait_for_image(image_path, timeout=10):
    start_time = time.time()
    while time.time() - start_time < timeout:
        try:
            location = pyautogui.locateOnScreen(
                image_path, region=(0, 0, 1360, 760), confidence=0.9, grayscale=True
            )
            if location:
                return location
        except pyautogui.ImageNotFoundException:
            pass
        time.sleep(1.5)
    return None


def wait_for_images(image_path, timeout=10):
    start_time = time.time()
    while time.time() - start_time < timeout:
        try:
            location = pyautogui.locateAllOnScreen(
                image_path, region=(0, 0, 1366, 760), confidence=0.9
            )
            if location:
                return list(location)
        except pyautogui.ImageNotFoundException:
            pass
        except pyscreeze.ImageNotFoundException:
            pass
        time.sleep(1.5)
    return []

def right_click():
    pyautogui.mouseDown(button="right")  # Perform a right-click
    time.sleep(0.1)  # Adjust the delay if needed (time in seconds)
    pyautogui.mouseUp(button="right")  # Release the right-click


def triple_click(x, y):
    for _ in range(3):
        pyautogui.click(x, y)
        time.sleep(0.1)

def get_data(obj):
    start_time = time.time()
    time.sleep(0.5)
    activate_window("Mozilla Firefox")
    time.sleep(0.5)
    # load webpage
    url_profile = obj["contact_name_href"]
    if url_profile.find("?") != -1:
        url_profile = url_profile[: url_profile.find("?")]
    logger.info(url_profile)
    # open a new tab
    # pyautogui.hotkey('ctrl', 't')
    pyautogui.hotkey("ctrl", "l")
    time.sleep(0.5)
    pyautogui.typewrite(url_profile)
    time.sleep(0.5)
    pyautogui.press("enter")
    time.sleep(8)

    # check page loaded
    image_path = os.path.join(script_dir, "img/ContactDetails.png")
    loaded = wait_for_image(image_path, 40)

    # Box(left=535, top=541, width=74, height=19)
    if loaded:
        logger.info("Page loaded")

    # LinkedIn
    image_path = os.path.join(script_dir, "img/LinkedIn.png")
    l = wait_for_images(image_path, 3)
    l_index = 0
    retry = 0
    temp_l = None
    while l_index < len(l):
        logger.info(f"l_index:{l_index+1}/{len(l)}")
        if temp_l is not None:
            if temp_l[0] == l[l_index][0]:
                l_index = l_index + 1
                continue
        li = l[l_index]
        pyautogui.moveTo(li, duration=2, tween=pyautogui.easeInOutQuad)
        time.sleep(0.5)
        pyautogui.click(li)
        time.sleep(2)
        pyautogui.hotkey("ctrl", "l")
        time.sleep(0.5)
        pyautogui.hotkey("ctrl", "c")
        time.sleep(0.5)
        pyautogui.hotkey("ctrl", "w")
        time.sleep(0.5)
        copied_text = pyperclip.paste()
        url = copied_text.strip()
        url = get_link_from_redirect(url)

        if url is not None and url != "":
            # logger.info(len(url))
            if url.find("linkedin") == -1:
                logger.info("not found 'linkedin'")
                if retry < 3:
                    retry = retry + 1
                else:
                    l_index = l_index + 1
                continue

            # personal LindedIn
            if li[0] < loaded_x_left:
                obj["LinkedIn_Personal_URL"] = url
            else:
                obj["LinkedIn_URL"] = url
        temp_l = li
        l_index = l_index + 1

    # Supplemental_Email
    image_path = os.path.join(script_dir, "img/Supplemental.png")
    l = wait_for_image(image_path, 3)
    if l:
        pyautogui.moveTo(
            l[0] + l[2] + 50 + random.randint(-5, 5),
            l[1] + l[3] / 2,
            duration=1,
            tween=pyautogui.easeInOutQuad,
        )
        time.sleep(1)
        right_click()
        time.sleep(1)
        pyautogui.hotkey("l")
        time.sleep(1)
        pyautogui.hotkey("esc")
        # [email protected]
        copied_text = pyperclip.paste()
        obj["Supplemental_Email"] = copied_text.strip()

    # Local address
    image_path = os.path.join(script_dir, "img/Local.png")
    l = wait_for_image(image_path, 3)
    if l:
        x = l[0] + l[2] + 30 + random.randint(-5, 5)
        pyautogui.moveTo(x, l[1] + l[3] / 2, duration=1, tween=pyautogui.easeInOutQuad)
        time.sleep(1)
        triple_click(x, l[1] + l[3] / 2)
        time.sleep(1)
        pyautogui.hotkey("ctrl", "c")
        time.sleep(1)
        # [email protected]
        copied_text = pyperclip.paste()
        obj["Local_Location_Address"] = copied_text.strip()

def main(max_id):
    failed_list = []
    for i in range(batch):
        logger.info(f"batch:{i+1}/{batch}")
        obj = get_job()
        if obj is None:
            continue
        if obj["contact_name_href"] == "" or obj["contact_name_href"] is None:
            res = post_job(obj)
            logger.info(res)
            continue

        if obj["id"] > max_id:
            logger.info("max_id reached")
            continue
        obj = get_data(obj)
        if obj["message"] != "success":
            if obj["id"] in failed_list:
                res = post_job(obj)
                logger.info(res)
            else:
                failed_list.append(obj["id"])
            continue

        res = post_job(obj)
        logger.info(res)

if __name__ == "__main__":
    # time.sleep(10)
    # countdown
    for i in range(6):
        logger.info(f"countdown:{6-i}")
        time.sleep(1)

    # get max id from command
    if len(sys.argv) > 1:
        max_id = int(sys.argv[1])
        logger.info(f"max_id:{max_id}")
    else:
        max_id = 9999999

    main(max_id)
分類
Linux

Fedora 35 失去支持後升級 37

Fedora 失去支持後

失去支持後,dnf upgrade 會報錯 "Failed to download metadata for repo 'updates'" 。可以修改 /etc/yum.repos.d/ 中如下四個文件:fedora.repo fedora-modular.repo fedora-updates.repo fedora-updates-modular.repo 中的兩行

baseurl=https://archives.fedoraproject.org/pub/archive/fedora/linux/updates/$releasever/Everything/$basearch/
#metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch

修改後便不再報錯,但是由於 35 已失去官方支持,所以不會再有更新。

升級 Fedora 系統

!畢竟是更新系統,所以重要資料記得備份。!

可以通過 dnf system-upgrade 來方便的升級到 36 或 37(但是不能直接升級到38)。升級系統請參考官方的 DNF System Upgrade 以及 Upgrade to Fedora 37 from Fedora 36 using DNF。大約要下載 2G 大小的文件,下載後升級過程需要半個小時左右。主要命令如下:

#更新一下,如果有更新之後要重啓一下
sudo dnf upgrade --refresh
#安裝系統升級 dnf 插件
sudo dnf install dnf-plugin-system-upgrade
#下載要升級到的版本,如 37
sudo dnf system-upgrade download --releasever=37
#下載完畢,執行系統更新
sudo dnf system-upgrade reboot

執行 system-upgrade download 前需要把前面四個文件改回去,不然 archive 站點是沒有新系統的資料包的。