Word 歸檔

在 Word 下整理聊天訊息的規範。
訊息在拷貝至 Word 時,已經携帶一定的格式。
該方法適用於未使用 QQNT 資料庫的 QQ 或 TIM 版本。

基本規範

WORD WPS文字
中文 / 西文字體:等綫
字形:加粗
字號:五號 / 10.5
字體顔色:
A: R 201 G 138 B 218(C98ADA)
B: R 112 G 173 B 71(70AD47)
效果:All Off
中文 / 西文字體:等綫
字形:加粗
字號:五號 / 10.5
字體顔色:
A: R 201 G 138 B 218(C98ADA)
B: 112 G 173 B 71(70AD47)
效果:All Def

為何使用 WPS ?
WPS 的大尺寸文件處理能力非常强大!
我在整理訊息的時候 Word 突然卡頓死亡,修復無果 : (
頁面 500 ,字數 160K ,這在過去只需等待十秒鐘便可加載完成。
啊啊,果然每次整理聊天訊息的時候,都是系統要重灌的時候呢(笑)


方案:Word + Pdf

  1. 聊天訊息拷貝至 Word;
  2. 使用 VBA 宏將 IMG 統一縮放至 40%
  3. Ctrl +C 全選内容,設置字號為五號;
  4. 匿稱替換,格式為^p匿稱 ,如 ^pMagstic 
    • ^p 為換行
    • 匿稱後的空格為  
    • 該轉換為匹配 常規訊息
  5. 匿稱替換,格式為匿稱 ,如 Magstic
    • ^p 為換行
    • 匿稱後的空格為  
    • 該轉換為匹配 訊息引用
  6. 校對訊息的缺漏;
    • 卡片訊息使用 Link 替代
    • 有效 / 失效文件使用 截圖替代
    • 失效 IMG 文件使用 Cache文件名追索並替換
    • 縮放圖像以美化排版
    • 未漫游的斷層訊息使用 QQMobile 的截圖
    • 若以上訊息有缺失,則標注【LOST】
  7. 設定文檔分欄為 2 ;
  8. 轉換為 Pdf ,和 Word 共同存儲。

方案:Word + Markdown

Word + Markdown 整理聊天訊息的規範。
結果上較為粗放,但提供了更高效的校對方式。
這亦是未來的個人備份聊天記錄的備選方案之一。

  1. 聊天訊息拷貝至 Word;
  2. 使用 VBA 宏將 IMG 統一縮放至 40%
  3. Ctrl +C 全選内容,設置字號為五號;
  4. 匿稱替換,格式為^p匿稱 ,如 ^pMagstic 
    • ^p 為換行
    • 匿稱後的空格為  
  5. 匿稱替換,格式為匿稱 ,如 Magstic
    • ^p 為換行
    • 匿稱後的空格為  
    • 該轉換為匹配 訊息引用
  6. 校對卡片訊息和失效文件;
    • 卡片訊息使用 Link替代
    • 有效 / 失效文件使用 【文件】Name.zip(12.34MB) 替代
    • 未漫游的斷層訊息使用 QQMobile 的截圖
    • 若以上訊息有缺失,則標注【LOST】
  7. 使用 Pandoc 將 Doc 轉為 Markdown,並使用 PowerShell 清理 Markdown 的無用標記;
  8. 使用 RAR 額外存檔媒體文件 ,和 Word 共同存儲。
    • 修復記錄:5%
    • 壓縮方式:僅儲存

相關指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Sub ImgSize()
Dim imgHeight As Single
Dim imgWidth As Single
Dim iShape As InlineShape
Dim n As Single

' 設置縮放比例,0.5 為縮放至原圖的 50%。
n = 0.5

For Each iShape In ActiveDocument.InlineShapes
' 鎖定寬高比
iShape.LockAspectRatio = msoTrue

' 獲取 IMG 寬高
imgHeight = iShape.Height
imgWidth = iShape.Width

' 調整 IMG 寬高
iShape.Height = n * imgHeight
iShape.Width = n * imgWidth
Next iShape
End Sub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Sub ImgSizeBatch()
Dim docPath As String
Dim docName As String
Dim doc As Document
Dim imgHeight As Single
Dim imgWidth As Single
Dim iShape As InlineShape
Dim n As Single

n = 0.4

docPath = "E:\File\2024\"
docName = Dir(docPath & "*.docx")

Do While docName <> ""
Set doc = Documents.Open(docPath & docName)

For Each iShape In doc.InlineShapes
iShape.LockAspectRatio = msoTrue
imgHeight = iShape.Height
imgWidth = iShape.Width
iShape.Height = n * imgHeight
iShape.Width = n * imgWidth
Next iShape

doc.SaveAs2 docPath & docName
doc.Close

docName = Dir()
Loop
End Sub
1
pandoc -s 2023.04.docx -o 2023.04.md --extract-media=2023.04 --wrap=none
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
$successCount = 0
$errorCount = 0
$errorFiles = @()

Write-Host @"
本程式將執行以下操作:
1. 掃描當前目錄下所有 MD 檔案
2. 清理檔案中的寬高標記
3. 移除所有反斜線
4. 使用 UTF-8 BOM 編碼重新保存

是否繼續? (Y/N)
"@ -ForegroundColor Yellow

$response = Read-Host
if ($response.ToUpper() -ne 'Y') {
Write-Host "操作已取消" -ForegroundColor Red
exit
}

$mdFiles = Get-ChildItem -Path $PSScriptRoot -Filter "*.md"

Write-Host "開始處理文件..." -ForegroundColor Cyan

foreach ($file in $mdFiles) {
try {
Write-Host "正在處理:" $file.Name

# 使用UTF8編碼讀取文件
$content = Get-Content -Path $file.FullName -Raw -Encoding UTF8

# 清理寬高標記
$newContent = $content -replace '{width="[^"]*"\s*height="[^"]*"}', ''

# 按行分割內容
$lines = $newContent -split "`r`n|`r|`n"

$processedLines = $lines | ForEach-Object {
$line = $_

# 清理行中所有的反斜線
$line = $line -replace '\\', ''

$line
}

# 重新組合內容
$finalContent = $processedLines -join "`n"

# 使用UTF8-BOM編碼保存
[System.IO.File]::WriteAllText($file.FullName, $finalContent, [System.Text.Encoding]::UTF8)

$successCount++
Write-Host "成功處理:" $file.Name -ForegroundColor Green
}
catch {
$errorCount++
$errorFiles += $file.Name
Write-Host "處理失敗:" $file.Name -ForegroundColor Red
Write-Host "錯誤信息:" $_.Exception.Message -ForegroundColor Red
}
}

Write-Host "`n處理完成!" -ForegroundColor Yellow
Write-Host "成功處理文件數:$successCount" -ForegroundColor Green
Write-Host "失敗文件數:$errorCount" -ForegroundColor Red

if ($errorFiles.Count -gt 0) {
Write-Host "`n失敗的文件列表:" -ForegroundColor Red
$errorFiles | ForEach-Object { Write-Host "- $_" -ForegroundColor Red }
}

Write-Host "`n按任意鍵退出..." -ForegroundColor Yellow
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

茶後小敘

個人一直在使用 TIM 3.4.8(22086) ,因此可以使用 Ctrl + A 全選消息管理器中的訊息。
在 QQNT 之後,這種方式已經無法使用。
現在個人使用的 TIM ,也已在 2025 年 無法離線漫游 聊天訊息。
我無法如 Geek 那般解密聊天資料庫,也無法時刻讓我的 PC 上的 TIM Online。
哈哈,更不必提舊版軟體時刻有被限制登入的風險。

不過幸運的是, QQNT 支援手動框選訊息,且可以拷貝!
這非常辛苦,不過爲了回憶留存,也無妨了。

現在有項目可以解密 QQNT 資料庫,但解碼歸檔始終無期。
多方權衡,我只能選擇這樣拙劣的方法。
順帶一提,文字替換其實也研究過 VBA 宏,只是無效……沒錯,那個 &#160; 空格。
啊,就這樣吧。
我想我還有 180K 條訊息要整理呢 : )


Markdown 歸檔

方案:Markdown

該方式適用於 QQNT ,亦是未來的備份聊天的至高備選。
QQNT 的資料庫足夠高效,以致於訊息斷層的可能性基本為零。
不足之處在於:每次至多手動框選 100 條訊息,且無法拷貝引用内容。

  1. 在 QQNT 中框選訊息;
    • 酌情框選,勿超過 100 條
  2. 使用 QQNT 的 “複製” 功能拷貝訊息;
  3. 在 VS Code 中粘貼訊息;

現在,訊息便可在 Obsidian 等 Markdown 編輯器中閲讀。
不過注意到了嗎?訊息中的 IMG 是絕對連結,這不利於歸檔。

  1. 使用 Ps1 指令將 HTML IMG 轉為 Markdown IMG,并設置相對連結;
    • 指令將 IMG 拷貝至同路徑下,以實現相對引用

若一直使用 QQNT 進行訊息收發,簡單校對 Files 便可結束。
若 QQNT 資料庫使用舊版資料庫轉化而來,則需校對無法正確引用的 IMG。

  1. 檢索 “暂不支持的消息类型” ,校對。
  2. 使用 RAR 存檔文件 ,且進行多重備份。
    • 修復記錄:5%
    • 壓縮方式:僅儲存

相關指令

簡易版,適用於純 QQNT。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
$scriptPath = $PSScriptRoot
$mdFiles = Get-ChildItem -Path $scriptPath -Filter "*.md"

function Convert-MDImages {
param(
[Parameter(Mandatory=$true)]
[string]$MDFilePath
)

try {
# 驗證檔案是否存在
if (-not (Test-Path $MDFilePath)) {
throw "檔案不存在: $MDFilePath"
}

# 建立媒體資料夾
$mdFileName = [System.IO.Path]::GetFileNameWithoutExtension($MDFilePath)
$mdDirectory = Split-Path $MDFilePath -Parent
$mediaPath = Join-Path $mdDirectory "$mdFileName\media"
New-Item -ItemType Directory -Force -Path $mediaPath | Out-Null

# 讀取內容
$content = Get-Content $MDFilePath -Raw -Encoding UTF8

# 比對並取代圖片
$pattern = '<img\s+src="file:\/\/([^"]+)"\s*\/>'
$newContent = [regex]::Replace($content, $pattern, {
param($match)

$originalPath = $match.Groups[1].Value
$fileName = [System.IO.Path]::GetFileName($originalPath)
$newImagePath = Join-Path $mediaPath $fileName

# 拷貝圖片檔案
if (Test-Path $originalPath) {
Copy-Item $originalPath $newImagePath -Force
return "![image](./$mdFileName/media/$fileName)"
}

Write-Warning "找不到圖片: $originalPath"
return $match.Value
})

# 儲存檔案
$newContent | Out-File $MDFilePath -Encoding UTF8 -Force

Write-Host "處理完成!檔案 $MDFilePath 的圖片已移動至: $mediaPath" -ForegroundColor Green
}
catch {
Write-Error "錯誤: $_"
}
}

foreach ($file in $mdFiles) {
Write-Host "正在處理檔案: $($file.FullName)" -ForegroundColor Yellow
Convert-MDImages -MDFilePath $file.FullName
}

複雜版,適用於非 QQNT。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
$scriptPath = $PSScriptRoot
$mdFiles = Get-ChildItem -Path $scriptPath -Filter "*.md"

function Convert-MDImages {
param(
[Parameter(Mandatory=$true)]
[string]$MDFilePath
)

try {
if (-not (Test-Path $MDFilePath)) {
throw "檔案不存在: $MDFilePath"
}

$mdFileName = [System.IO.Path]::GetFileNameWithoutExtension($MDFilePath)
$mdDirectory = Split-Path $MDFilePath -Parent
$mediaPath = Join-Path $mdDirectory "$mdFileName\media"
New-Item -ItemType Directory -Force -Path $mediaPath | Out-Null

$content = Get-Content $MDFilePath -Raw -Encoding UTF8

$pattern = '<img\s+src="file:\/\/([^"]+)"\s*\/?>'
$newContent = $content

[regex]::Matches($content, $pattern) | ForEach-Object {
$match = $_
$originalPath = $match.Groups[1].Value

$escapedPath = [Management.Automation.WildcardPattern]::Escape($originalPath)
$fileName = [System.IO.Path]::GetFileName($originalPath)
$safeFileName = [System.IO.Path]::GetFileNameWithoutExtension($fileName) + [System.IO.Path]::GetExtension($fileName)
$newImagePath = Join-Path $mediaPath $safeFileName

if (Test-Path $escapedPath) {
Copy-Item $escapedPath $newImagePath -Force
$replacement = "![image](./$mdFileName/media/$safeFileName)"
$newContent = $newContent.Replace($match.Value, $replacement)
}
else {
Write-Warning "圖像尋找失敗: $originalPath"
}
}

$newContent | Out-File $MDFilePath -Encoding UTF8 -Force

Write-Host "MD 中的 HTMLIMG 已處理完畢!檔案 $MDFilePath 的 IMG 已移動至: $mediaPath" -ForegroundColor Green
}
catch {
Write-Error "ERROR: $_"
}
}

foreach ($file in $mdFiles) {
Write-Host "正在處理檔案: $($file.FullName)" -ForegroundColor Yellow
Convert-MDImages -MDFilePath $file.FullName
}

茶後小敘

這時,我已歸檔了全部訊息,可以正式開始資料備份。
在歸檔過程中,我遭遇無數阻力,這是程式碼無法解析的,只能人力精校。

現在一切都結束了嗎?或許不是的。
表情貼圖的大小不一,造成 Markdown 的閲讀體驗不會很好。

現在粗略的解決方案是建立一個貼圖庫,透過 MD5 校驗來定位表情貼圖。
最後使用批處理的手段將貼圖縮放,而後批量替換 Markdown 中的貼圖路徑。

非常笨拙的手段,呵呵。不過也別無他法。
圖像識別手段現在尚且羸弱,無法支持我的需求。

好了,就這樣,下次見: )
我會帶著嶄新的作業系統來寫 Blog XD