weflow-cli 1.6.3 → 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/OPERATIONS.md +1 -1
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/docs/HEALTH-CHECK.md +6 -5
- package/docs/MCP.md +1 -1
- package/docs/NPM-PUBLISH.md +12 -4
- package/docs/PROJECT_STATE.md +4 -4
- package/docs/SETUP.md +1 -1
- package/package.json +3 -3
- package/scripts/export_chat_html.py +66 -14
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@ The npm package is published separately from GitHub. It may lag behind the `mast
|
|
|
4
4
|
|
|
5
5
|
All notable user-facing changes are recorded here. This project follows [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## 1.6.4
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Restored four pieces of matching logic in the HTML exporter that the `90b165a` clone-consolidation merge had silently dropped, all of which lowered media coverage without failing loudly:
|
|
12
|
+
- the `unique:<local_id>` fallback, which matches an image when that id resolves to exactly one distinct picture in the conversation (content-deduped, so the shard-collision risk that rules out a bare `local_id` does not apply);
|
|
13
|
+
- `is_encoded_media_type`, needed because WeChat stores some forwards as high-bit variants of type 49 — the mask turns those into a plain 49, which is a registered type, so the branch guarding them had become unreachable;
|
|
14
|
+
- the guard that keeps a type-49 row carrying a title or url on the link-card path instead of hiding it behind a cached thumbnail;
|
|
15
|
+
- the cache-first lookup in the article branch, which had been downloading covers it already had on disk.
|
|
16
|
+
- Seeded `COVER_STATE`'s budgets with their limits instead of 0. They were only ever set by `main()`, so any caller reaching the fetchers another way silently skipped every remote fetch and got `None` back with no error.
|
|
17
|
+
- `pipeline_security_test` no longer reads the developer's real `~/.weflow-cli/config.json`; it points `CONFIG_PATH` at an empty temp file. The test only passed on a machine that happened to have a config, and reading a real config from a unit test is exactly what the repo rules forbid.
|
|
18
|
+
- CI actually runs, for the first time. `npm test` passed locally and failed on every push: the script used `test/**/*.test.ts`, which Git Bash expands locally but CI's bash does not (globstar is off by default), so Node received the literal pattern and reported `Could not find ...`. The glob is now quoted so Node expands it. The single combined job was also split into `node` / `python` / `release-consistency`, because a Node failure previously stopped the Python tests from running at all — they had been failing unnoticed.
|
|
19
|
+
- Raised the Node floor to 22.13.0. `src/core/sqlcipherCore.ts` imports `node:sqlite` at module scope; that builtin arrives in 22.5.0 and stops needing `--experimental-sqlite` at 22.13.0, so on Node 18/20 every command died at load while `engines` still claimed `>=18`. Both are past end-of-life, so the floor moved rather than adding a lazy-load path for dead runtimes.
|
|
20
|
+
- Rebuilt `package-lock.json`, which still said 1.6.1 and still listed `lz4` as an ordinary dependency after the 1.6.3 change moved it to optional. `npm audit fix` then took production vulnerabilities from 17 to 6; the remainder (`@xmldom/xmldom`, `exceljs`, `@wenyan-md/core`, `mermaid`, `speech-rule-engine`, `uuid`) have no non-breaking fix — `exceljs@4.4.0` and `@wenyan-md/core@3.0.11` are already the newest releases and the advisories' suggested "fix" is to downgrade them.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `docs/PROJECT_STATE.md` no longer carries a hardcoded version — it points at `package.json`, which is what drifted to `1.5.1`. Example version numbers in `docs/NPM-PUBLISH.md` and `docs/HEALTH-CHECK.md` became placeholders for the same reason.
|
|
25
|
+
- Git tags and GitHub Releases for `1.6.0`, `1.6.1` and `1.6.3`, so npm versions map to commits. `1.6.2` has neither: it was published, but its version bump was never committed.
|
|
26
|
+
|
|
7
27
|
## 1.6.3
|
|
8
28
|
|
|
9
29
|
### Fixed
|
package/OPERATIONS.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
| 依赖 | 用途 | 检查 |
|
|
8
8
|
| --- | --- | --- |
|
|
9
|
-
| Node.js
|
|
9
|
+
| Node.js 22.13+ | CLI、MCP、构建 | `node --version` |
|
|
10
10
|
| Python 3.10+ | NT 数据、日报、阅读器 | `python --version` |
|
|
11
11
|
| `requirements.txt` | 标准 4.x 工作流 | `python -m pip install -r requirements.txt` |
|
|
12
12
|
| `requirements-3x.txt` | 旧版 3.x 数据,可选 | `python -m pip install -r requirements-3x.txt` |
|
package/README.en.md
CHANGED
|
@@ -78,7 +78,7 @@ weflow-cli mcp-config # one-shot MCP client integration
|
|
|
78
78
|
|
|
79
79
|
### 1. Install and Check
|
|
80
80
|
|
|
81
|
-
Requires Node.js
|
|
81
|
+
Requires Node.js 22.13+, Python 3.10+, and a signed-in Windows WeChat. After installation, check your environment first:
|
|
82
82
|
|
|
83
83
|
```powershell
|
|
84
84
|
npm install -g weflow-cli
|
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ Linux 的自动初始化能力取决于微信发行版、Python 依赖和当前
|
|
|
108
108
|
|
|
109
109
|
> npm 包与 GitHub 发布分开进行;需要最新修复时,请先确认 npm 包版本,或直接使用 GitHub `master` 分支代码。
|
|
110
110
|
|
|
111
|
-
需要 Node.js
|
|
111
|
+
需要 Node.js 22.13+、Python 3.10+,以及已登录的 Windows 微信。安装后先检查本机环境:
|
|
112
112
|
|
|
113
113
|
```powershell
|
|
114
114
|
npm install -g weflow-cli
|
package/docs/HEALTH-CHECK.md
CHANGED
|
@@ -15,16 +15,17 @@ py scripts/health_check.py # 人读的报告
|
|
|
15
15
|
py scripts/health_check.py --json # 机器读
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
退出码 `0` = 全部通过,`1` =
|
|
18
|
+
退出码 `0` = 全部通过,`1` = 有失败项。报告长这样(版本号、日期、字节数都是
|
|
19
|
+
当次运行的真实值,仅作示例):
|
|
19
20
|
|
|
20
21
|
```
|
|
21
|
-
WeFlow 健康检查 · 2026-09-
|
|
22
|
+
WeFlow 健康检查 · 2026-09-19 10:04
|
|
22
23
|
|
|
23
|
-
[OK ] 版本一致 CLI
|
|
24
|
+
[OK ] 版本一致 CLI X.Y.Z / package X.Y.Z
|
|
24
25
|
[OK ] 环境自检 已初始化=True 消息库=True
|
|
25
26
|
[OK ] Python 依赖 齐全
|
|
26
|
-
[OK ] 会话新鲜度 最新会话 2026-09-
|
|
27
|
-
[OK ] 读取路径一致 (wxid_6xx1ehe4h…) json 最新 2026-09-
|
|
27
|
+
[OK ] 会话新鲜度 最新会话 2026-09-19 10:03(0.0 天前)
|
|
28
|
+
[OK ] 读取路径一致 (wxid_6xx1ehe4h…) json 最新 2026-09-19 10:03 / html 最新 2026-09-19 10:03:12
|
|
28
29
|
[OK ] 导出格式 json 938 字节
|
|
29
30
|
[OK ] 导出格式 txt 145 字节
|
|
30
31
|
[OK ] 导出格式 excel 6788 字节
|
package/docs/MCP.md
CHANGED
|
@@ -92,4 +92,4 @@ The chat-data tools share service code with the optional `weflow-cli assistant`
|
|
|
92
92
|
| --- | --- |
|
|
93
93
|
| Server fails to start | Run `npm install` in the configured `cwd`, then run `npx tsx mcp-server/index.ts`. |
|
|
94
94
|
| No articles found | Generate a daily collection first, then confirm `output/biz-daily/` exists under `cwd`. |
|
|
95
|
-
| Client cannot find `npx` | Configure an absolute Node.js command path or install Node.js
|
|
95
|
+
| Client cannot find `npx` | Configure an absolute Node.js command path or install Node.js 22.13+. |
|
package/docs/NPM-PUBLISH.md
CHANGED
|
@@ -26,9 +26,17 @@ grep -c " dist/" /tmp/packlist.txt # 注意: 行首是体积, 不是路径
|
|
|
26
26
|
|
|
27
27
|
**别信命令的输出,信 registry 的 `versions` 表:**
|
|
28
28
|
|
|
29
|
+
下面用 `X.Y.Z` 代表这次要发的版本,**照抄时换成实际版本号**——写死在这里的版本
|
|
30
|
+
过几天就是错的。
|
|
31
|
+
|
|
29
32
|
```bash
|
|
33
|
+
V=X.Y.Z
|
|
30
34
|
curl -s "https://registry.npmjs.org/weflow-cli?nocache=$RANDOM" --max-time 30 \
|
|
31
|
-
| python -c "
|
|
35
|
+
| python -c "
|
|
36
|
+
import json,sys,os
|
|
37
|
+
d=json.load(sys.stdin)
|
|
38
|
+
print('latest:', d['dist-tags']['latest'])
|
|
39
|
+
print('目标版本存在:', os.environ['V'] in d['versions'])" V="$V"
|
|
32
40
|
```
|
|
33
41
|
|
|
34
42
|
- `目标版本存在: True` → **发布成功**,剩下只是传播
|
|
@@ -40,11 +48,11 @@ curl -s "https://registry.npmjs.org/weflow-cli?nocache=$RANDOM" --max-time 30 \
|
|
|
40
48
|
|
|
41
49
|
| 现象 | 含义 | 动作 |
|
|
42
50
|
| --- | --- | --- |
|
|
43
|
-
| `+ weflow-cli@
|
|
51
|
+
| `+ weflow-cli@X.Y.Z` | 已被接受 | 继续,等传播 |
|
|
44
52
|
| `being processed and may take a few minutes` | 正常提示 | 等,别重发 |
|
|
45
53
|
| `npm view` 仍是旧版本 | CDN 传播中 | **不是失败**,等 2–5 分钟 |
|
|
46
54
|
| 重发报 `409 Conflict - Cannot publish over previously staged version` | **已经发布成功了** | 别再发,去等传播 |
|
|
47
|
-
| tarball `404`(`/-/weflow-cli-
|
|
55
|
+
| tarball `404`(`/-/weflow-cli-X.Y.Z.tgz`) | 元数据已发布,tarball 还在传播 | 等,不是失败 |
|
|
48
56
|
| `install` 报 `ETARGET No matching version` | 客户端缓存了旧 packument | 加 `--prefer-online` 重试 |
|
|
49
57
|
| `install` 报 `404` | 元数据在、tarball 未到 | 等传播 |
|
|
50
58
|
| `403 ... bypass 2fa enabled is required` | token 没勾 Bypass 2FA | 重新生成 token(见第 3 节) |
|
|
@@ -90,7 +98,7 @@ npm view weflow-cli version --registry=https://registry.npmmirror.com
|
|
|
90
98
|
|
|
91
99
|
```bash
|
|
92
100
|
mkdir -p /tmp/verify && cd /tmp/verify && npm init -y >/dev/null
|
|
93
|
-
npm install weflow-cli@
|
|
101
|
+
npm install weflow-cli@X.Y.Z --registry=https://registry.npmjs.org --prefer-online --no-audit --no-fund
|
|
94
102
|
node -e "console.log(require('./node_modules/weflow-cli/package.json').version)"
|
|
95
103
|
```
|
|
96
104
|
|
package/docs/PROJECT_STATE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Project State
|
|
2
2
|
|
|
3
|
-
> Last reviewed: 2026-09-
|
|
3
|
+
> Last reviewed: 2026-09-19. This is the current maintenance snapshot, not a release note. Keep it factual and update it with meaningful project changes.
|
|
4
4
|
|
|
5
5
|
## Purpose
|
|
6
6
|
|
|
@@ -8,8 +8,8 @@ WeFlow CLI is a local-first command-line tool and MCP server for user-authorized
|
|
|
8
8
|
|
|
9
9
|
## Current Baseline
|
|
10
10
|
|
|
11
|
-
- Source package version: `
|
|
12
|
-
- Runtime: Node.js
|
|
11
|
+
- Source package version: take it from `package.json` -> `version` rather than from this file - a hardcoded copy drifts silently, and `package-lock.json` must always match it. The npm registry may remain on an earlier version until a maintainer publishes a release.
|
|
12
|
+
- Runtime: Node.js 22.13+; Python 3.10+ is required for database and daily-reading workflows. The floor is set by `src/core/sqlcipherCore.ts`, which imports `node:sqlite` at module scope; that builtin exists from 22.5.0 and stops needing `--experimental-sqlite` at 22.13.0.
|
|
13
13
|
- Main development command: `npm run dev -- <command>`.
|
|
14
14
|
- Compiled/package command: `node cli.cjs <command>`; CLI, MCP, database, export, and assistant services resolve resources from the same package root in both source and compiled layouts.
|
|
15
15
|
- Build check: `npm run build`.
|
|
@@ -53,7 +53,7 @@ Date-bounded message exports and `wechat.export_messages` page through the selec
|
|
|
53
53
|
- `search` and `chat` use the same preview/confirmation boundary. Queries, questions, and optional conversation restrictions are inherited through the worker environment instead of appearing in process arguments; machine callers cannot start interactive RAG chat.
|
|
54
54
|
- `daily-server --status --json` remains read-only. Machine startup uses `daily-server --dry-run --json` followed by `daily-server --yes --json`; the confirmed process is detached and remains bound to loopback. The legacy `fav-server` compatibility entry enforces the same preview and confirmation rules.
|
|
55
55
|
|
|
56
|
-
Documentation was synchronized with the
|
|
56
|
+
Documentation was synchronized with the current source baseline on 2026-09-19. Command behavior is defined by `bin/weflow-cli.ts`; detached reader startup waits for service readiness, and release packages can lag behind the GitHub source until published.
|
|
57
57
|
|
|
58
58
|
## Active Constraints
|
|
59
59
|
|
package/docs/SETUP.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weflow-cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.4",
|
|
4
4
|
"description": "让 AI 读懂你的微信 — 公众号日报·聊天导出·知识库搜索·MCP Server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsc",
|
|
46
|
-
"test": "node --import tsx --test test/**/*.test.ts",
|
|
46
|
+
"test": "node --import tsx --test \"test/**/*.test.ts\"",
|
|
47
47
|
"dev": "npx tsx bin/weflow-cli.ts",
|
|
48
48
|
"start": "node cli.cjs",
|
|
49
49
|
"prepublishOnly": "npm run build"
|
|
@@ -78,6 +78,6 @@
|
|
|
78
78
|
"typescript": "^5.7.0"
|
|
79
79
|
},
|
|
80
80
|
"engines": {
|
|
81
|
-
"node": ">=
|
|
81
|
+
"node": ">=22.13.0"
|
|
82
82
|
}
|
|
83
83
|
}
|
|
@@ -72,10 +72,21 @@ except Exception:
|
|
|
72
72
|
# Remote media is the single slowest step of an export: each miss costs a page
|
|
73
73
|
# fetch plus an image download, and inline thumbnails alone run to several
|
|
74
74
|
# hundred per conversation. Bound it per run and keep results on disk, misses
|
|
75
|
-
# included, so re-exports are instant.
|
|
75
|
+
# included, so re-exports are instant.
|
|
76
|
+
#
|
|
77
|
+
# Two separate pools: share-page covers are rare and expensive, inline appmsg
|
|
78
|
+
# thumbnails are common and cheap. Sharing one would let a run of thumbnails
|
|
79
|
+
# starve the covers that actually change how a page looks.
|
|
80
|
+
COVER_FETCH_LIMIT = 60
|
|
81
|
+
THUMB_FETCH_LIMIT = 300
|
|
82
|
+
|
|
83
|
+
# Seeded with the full budgets rather than 0: main() re-sets them, but anything
|
|
84
|
+
# that reaches the fetchers without going through main() used to find a budget
|
|
85
|
+
# of 0 and silently skip every remote fetch, returning None with no error.
|
|
76
86
|
COVER_STATE = {
|
|
77
|
-
'dir': '', 'budget':
|
|
78
|
-
'thumb_budget':
|
|
87
|
+
'dir': '', 'budget': COVER_FETCH_LIMIT, 'fetched': 0, 'cached': 0, 'skipped': 0,
|
|
88
|
+
'thumb_budget': THUMB_FETCH_LIMIT,
|
|
89
|
+
'thumb_fetched': 0, 'thumb_cached': 0, 'thumb_skipped': 0,
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
# Off by default: full-resolution originals are 5-24MB / 3000-5700px each, and
|
|
@@ -93,12 +104,6 @@ COVER_HEAD_BYTES = 64 * 1024
|
|
|
93
104
|
# cost time, which matters across thousands of media entries.
|
|
94
105
|
EMBED_SHRINK_THRESHOLD = 400 * 1024
|
|
95
106
|
|
|
96
|
-
# Two separate budgets: share-page covers are rare and expensive, inline
|
|
97
|
-
# appmsg thumbnails are common and cheap. Sharing one pool lets a run of
|
|
98
|
-
# thumbnails starve the covers that actually change how a page looks.
|
|
99
|
-
COVER_FETCH_LIMIT = 60
|
|
100
|
-
THUMB_FETCH_LIMIT = 300
|
|
101
|
-
|
|
102
107
|
# Set up by main(); custom stickers decrypted from WeChat's local cache.
|
|
103
108
|
STICKER_STATE = {'key': b'', 'dirs': [], 'cache_dir': ''}
|
|
104
109
|
|
|
@@ -386,6 +391,29 @@ def scan_nt_cache(nt_cache_dir, talker, account_dir='', own_wxid=''):
|
|
|
386
391
|
if key.startswith('md5:'):
|
|
387
392
|
image_map.setdefault(key, image)
|
|
388
393
|
|
|
394
|
+
# `unique:<local_id>`: an id that resolves to exactly one distinct image in
|
|
395
|
+
# this conversation is safe to match on its own. The id restarts across
|
|
396
|
+
# shards, but uniqueness inside one conversation's scan rules that out -
|
|
397
|
+
# which is what separates this from the bare-local_id match that
|
|
398
|
+
# get_cached_image deliberately refuses. Deduped by content, so one picture
|
|
399
|
+
# cached at several timestamps still counts as unique.
|
|
400
|
+
local_candidates = {}
|
|
401
|
+
for key, image in image_map.items():
|
|
402
|
+
if isinstance(key, str) and key.startswith('pair:'):
|
|
403
|
+
parts = key.split(':', 2)
|
|
404
|
+
if len(parts) != 3:
|
|
405
|
+
continue
|
|
406
|
+
local_id = parts[1]
|
|
407
|
+
elif isinstance(key, int):
|
|
408
|
+
local_id = str(key)
|
|
409
|
+
else:
|
|
410
|
+
continue
|
|
411
|
+
digest = hashlib.sha256(image[0].encode('ascii')).hexdigest()
|
|
412
|
+
local_candidates.setdefault(local_id, {})[digest] = image
|
|
413
|
+
for local_id, candidates in local_candidates.items():
|
|
414
|
+
if len(candidates) == 1:
|
|
415
|
+
image_map[f'unique:{local_id}'] = next(iter(candidates.values()))
|
|
416
|
+
|
|
389
417
|
return image_map
|
|
390
418
|
|
|
391
419
|
|
|
@@ -734,7 +762,9 @@ def get_cached_image(image_map, local_id, create_time, content='', resource_md5s
|
|
|
734
762
|
|
|
735
763
|
Deliberately *not* falling back to a bare `local_id`: that id restarts in
|
|
736
764
|
every message shard, so matching on it alone attaches one conversation's
|
|
737
|
-
image to another conversation's message.
|
|
765
|
+
image to another conversation's message. `unique:<local_id>` is the sound
|
|
766
|
+
version of that idea - scan_nt_cache only emits it when the id maps to a
|
|
767
|
+
single distinct image, so uniqueness has already ruled the collision out.
|
|
738
768
|
"""
|
|
739
769
|
if not image_map:
|
|
740
770
|
return None
|
|
@@ -750,6 +780,12 @@ def get_cached_image(image_map, local_id, create_time, content='', resource_md5s
|
|
|
750
780
|
return None
|
|
751
781
|
if image_map.get(pair_key):
|
|
752
782
|
return shrink_embedded(image_map[pair_key])
|
|
783
|
+
try:
|
|
784
|
+
unique_key = f'unique:{int(local_id)}'
|
|
785
|
+
except (TypeError, ValueError):
|
|
786
|
+
return None
|
|
787
|
+
if image_map.get(unique_key):
|
|
788
|
+
return shrink_embedded(image_map[unique_key])
|
|
753
789
|
return None
|
|
754
790
|
|
|
755
791
|
|
|
@@ -1560,8 +1596,14 @@ def format_message(row, talker, wx_dir, image_map=None, sender_map=None, display
|
|
|
1560
1596
|
local_id = row[0] or 0
|
|
1561
1597
|
server_id = row[1] or 0
|
|
1562
1598
|
local_type = row[2] or 0
|
|
1599
|
+
# WeChat stores some forwards as high-bit variants of type 49. The mask
|
|
1600
|
+
# below turns those into plain 49, which is a known type - so the cached
|
|
1601
|
+
# image branch further down can only recognise them if the pre-mask value
|
|
1602
|
+
# is kept. Dropping this made that branch unreachable.
|
|
1603
|
+
raw_local_type = local_type
|
|
1563
1604
|
if local_type > 0xffffffff:
|
|
1564
1605
|
local_type &= 0xffffffff
|
|
1606
|
+
is_encoded_media_type = raw_local_type > 0xffffffff and local_type == 49
|
|
1565
1607
|
real_sender_id = row[4] or 0
|
|
1566
1608
|
create_time = row[5] or 0
|
|
1567
1609
|
source = row[7]
|
|
@@ -1611,6 +1653,11 @@ def format_message(row, talker, wx_dir, image_map=None, sender_map=None, display
|
|
|
1611
1653
|
if content and content != source_text:
|
|
1612
1654
|
metadata_parts.append(content)
|
|
1613
1655
|
metadata_content = '\n'.join(metadata_parts)
|
|
1656
|
+
# A type-49 row carrying a title or url is a link/article card, not a bare
|
|
1657
|
+
# image. Without this guard the encoded-media fallback below would hide the
|
|
1658
|
+
# card (and its title/link) behind a cached thumbnail.
|
|
1659
|
+
has_app_link_metadata = local_type == 49 and bool(
|
|
1660
|
+
extract_xml_text(content, 'title') or extract_xml_text(content, 'url'))
|
|
1614
1661
|
is_emoji_xml = bool(re.search(r'<(?:msg\s*>)?\s*<emoji\b|<emoji\b', metadata_content, re.IGNORECASE))
|
|
1615
1662
|
is_contact_card = bool(
|
|
1616
1663
|
re.search(r'<msg\b[^>]*(?:nickname|username)=', metadata_content, re.IGNORECASE)
|
|
@@ -1694,7 +1741,9 @@ def format_message(row, talker, wx_dir, image_map=None, sender_map=None, display
|
|
|
1694
1741
|
if img_data:
|
|
1695
1742
|
image_b64 = img_data
|
|
1696
1743
|
display += f'<br><img src="data:{mime};base64,{img_data}" loading="lazy" />'
|
|
1697
|
-
elif local_type not in MSG_TYPES
|
|
1744
|
+
elif (local_type not in MSG_TYPES or is_encoded_media_type) \
|
|
1745
|
+
and not has_app_link_metadata \
|
|
1746
|
+
and get_cached_image(image_map, local_id, create_time, content, resource_md5s):
|
|
1698
1747
|
# Some image messages use encoded types (e.g. 21474836529 = images in appmsg)
|
|
1699
1748
|
# Check image_map for any message type
|
|
1700
1749
|
img_data, mime = get_cached_image(image_map, local_id, create_time, content, resource_md5s)
|
|
@@ -1787,11 +1836,14 @@ def format_message(row, talker, wx_dir, image_map=None, sender_map=None, display
|
|
|
1787
1836
|
builtin_title = _face_index.find_face(title)
|
|
1788
1837
|
title_html = (render_builtin_emoji(title, builtin_title)
|
|
1789
1838
|
if builtin_title else escape_html(title))
|
|
1790
|
-
#
|
|
1839
|
+
# Try the conversation cache before the network: a cached
|
|
1840
|
+
# thumbnail is already on disk and costs nothing, and without
|
|
1841
|
+
# this the article branch downloaded covers it already had.
|
|
1791
1842
|
thumb_url = extract_appmsg_image(content)
|
|
1792
|
-
|
|
1843
|
+
img_data = get_cached_image(image_map, local_id, create_time, metadata_content, resource_md5s)
|
|
1844
|
+
if not img_data and thumb_url:
|
|
1793
1845
|
img_data = download_image_as_base64(thumb_url, extract_media_aes_key(content))
|
|
1794
|
-
|
|
1846
|
+
if not img_data:
|
|
1795
1847
|
page_url = extract_xml_text(content, 'url')
|
|
1796
1848
|
img_data = download_bilibili_cover(page_url)
|
|
1797
1849
|
if not img_data:
|