逆向京麦客户端提取京东聊天表情映射表
本文全部内容由Opus 4.7 技术总结生成
最近在做电商客服质检的项目,对接京东平台拉取客服和顾客的聊天记录,准备喂给 LLM 分析意图、打标签。跑起来看样本数据,发现消息文本里经常混着 #E-s38、#E-b07、#E-a10 这种奇怪的字符串。
一开始以为是乱码或者没处理干净的转义,仔细看才意识到这是京东自有的表情符号占位。最头疼的是,翻遍京东开放平台文档,压根没给映射表。
表情占位符虽然是小问题,但是还是很值得处理下。
前端展示聊天记录时,客服和顾客都很常用表情,满屏 #E-xxx 字符非常影响阅读,显得不专业,重要的是不够帅。
找映射表的几条路
1. 开放平台文档
先翻官方文档,从 京东服务平台 到咚咚 IM 的 API 文档基本翻了一遍,没有表情映射相关内容。消息结构里只有 content 字段,表情以占位符形式直接存在于文本中,文档也没说怎么渲染。
放弃,再提工单,回复”需要先和咚咚产品确认表情数据是否能透出”。这种肯定是遥遥无期的。
2. 关键线索:客户端自己渲染
试着把 #E-s38 这个字符串直接复制到京麦客户端(咚咚)的聊天输入框里,回车发送 —— 客户端直接把它渲染成了一个表情图片。
这说明映射关系就藏在客户端本地。不需要猜,不需要抓包,直接扒客户端资源就行。
扒客户端资源
京麦 PC 端有 Windows 和 Mac 两个版本。我用的 Mac,先在 /Applications 下找:
1 | |
然后看 .app 的结构:
1 | |
有 Frameworks、Plugins、translations 这些目录,还有 .qm 文件,一看就是 Qt 应用,不是 Electron。Electron 最好扒(app.asar 解压就出源码),Qt 应用就得看资源文件散在哪。
定位表情目录
直接全局搜 emoji/face/smile 这些关键词:
1 | |
命中:
1 | |
注意这里有个套娃:京麦 app 里嵌了一个”咚咚工作台” app,表情资源在子 app 里面。face2x 和 face3x 对应普通屏和 Retina 屏的高清图。
看看里面有啥:
1 | |
一共 112 个 PNG,分 s/b/j 三组。图片是有了,但是关键的英文代码到中文含义的映射还在别的地方。
找映射关系
接着在咚咚工作台的 Resources 下翻:
1 | |
jsfile/ 下面有几个子目录:chathtml、receptionHtml、monitorjs 等。既然客户端是用 HTML + JS 渲染聊天区(Qt 内嵌 WebEngine 很常见),映射关系大概率就在这些 JS 里。
直接全局搜 E-s:
1 | |
命中一批文件,其中 receptionHtml/index.23385b17.js 最可疑(命名带哈希,明显是前端构建产物)。
提取映射表
JS 是压缩过的,一行几百 KB。用 Python 取 #E-s01 附近的上下文看看:
1 | |
输出一段关键代码:
1 | |
一箭双雕:
- 拿到了客户端识别表情的正则
/#E-[a-z]\d{2}/ - 拿到了完整映射表:code →
[中文名]
Unicode 转义就是中文。爱心 = 爱心,安慰 = 安慰,以此类推。
正则一把梭
写个脚本把所有映射掏出来:
1 | |
执行完拿到 105 条完整映射表,分 3 套前缀:
s系列 74 个:主力表情(微笑、大哭、拜拜、…)b系列 25 个:手势/业务相关(抱拳、京豆、优惠券、…)a系列 6 个:动态表情(给力、庆祝、红包、…)
前端接入
拿到 JSON 之后,在 React 项目里搞个工具文件,然后在消息渲染组件里挂上就行。
src/lib/jdEmoji.ts:
1 | |
注意 fallback 的设计:如果将来京东加了新表情(比如 #E-s95),没命中映射表就统一降级为 [表情],不会把原始的 #E-xxx 漏给用户看。
消息气泡里接入:
1 | |
顺序上要注意:图片消息的特殊格式(\P(url))不能走表情替换,否则 URL 里万一有字符匹配了正则就会误伤。先判断是不是图片,再做替换。
几点总结
一、客户端渲染的 = 客户端本地有数据。任何你在客户端能看到正确渲染的东西,资源必然在本地某处,只是翻得勤不勤。表情、图标、音效、甚至有些业务枚举都是这个套路。
二、先定应用类型再动手。Electron 的 app.asar 解压即源码;Qt 的 .app 需要找 Resources 目录和 qrc 资源,可能还需要 strings 和 grep 配合;Flutter 就得动 libflutter.so 和 snapshot 了,那个难很多。京麦是 Qt + WebEngine 的混合架构,所以最终映射表落在了 JS 文件里,反而比纯 Qt 好扒。
三、逆向思路要顺着渲染管线走。客户端识别表情 → 渲染图片,那映射逻辑必然在”识别”和”渲染”之间。grep 一个已知的表情代码 #E-s01,就能快速定位到逻辑代码,比盲目读代码快得多。
四、给未知留 fallback。扒出来的映射表是”此刻”的版本,京东随时可能加新表情。生产代码里必须有降级策略,别硬编码假设所有 code 都在表里。
最后感谢京东客户端开发团队没做代码混淆(至少变量名是 f1e 但数据结构是纯明文),否则这事情得多花几个小时。