# LcMurmur
**Repository Path**: lczld/LcMurmur
## Basic Information
- **Project Name**: LcMurmur
- **Description**: Typecho 轻量级微语/说说插件
- **Primary Language**: PHP
- **License**: MIT
- **Default Branch**: master
- **Homepage**: https://www.lczld.cn/murmur/
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-10-19
- **Last Updated**: 2026-10-20
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# LcMurmur —— Typecho 轻量级微语/说说插件
支持文字、图片(1–9 张自动网格)、音乐、视频在线播放,带微信助理、点赞、评论回复、置顶、游客发帖与管理员审核。
- 作者:就是老陈 · 主页: · 版本:1.0.7
- 环境:Typecho 1.3.0+,PHP 7.4+(已在 PHP 8.3 实测)
---
## 一、安装
1. 解压得到 `LcMurmur` 目录,整个上传到站点的 `usr/plugins/` 下,最终路径为 `usr/plugins/LcMurmur/Plugin.php`。
2. 后台 → 控制台 → 插件,启用 **LcMurmur**(自动建表、注册路由与后台菜单、写入默认配置)。
3. 点「设置」按需调整,访问 `https://你的站点/index.php/murmur/`。
4. 以后升级不用再传压缩包:在 **微语墙** 面板顶部点「检查更新」「立即更新」即可。
| 地址 | 说明 |
| --- | --- |
| `/index.php/murmur/` | 微语墙首页 |
| `/index.php/murmur/type/text/` | 按类型筛选,可选 `text` / `image` / `music` / `video` |
| `/index.php/murmur/page/2/` | 分页 |
| `/index.php/murmur/?m=12` | 定位到某条微语 |
| `/index.php/murmur-api/` | 前台接口(发布、点赞、评论、加载更多、微信回调) |
后台菜单「微语」下两个面板:**微语墙**(筛选、批量通过/驳回/删除/置顶)与 **待审核**(待审微语 + 待审评论)。
---
## 二、前台
**发布**:点顶栏相机图标,底部滑出编辑层。文字里 `#话题` 会高亮,点一下就是按这个词筛选;图片每行一个地址、最多 9 张,也能本地上传;音乐填歌名/歌手/封面/音频;视频填 MP4 地址。表情是 😊 弹出的 99 个微信表情,图打包在插件里,不联网也能用,正文与评论里的 `[名称]` 自动显示成表情小图。
**互动**:点赞(同一浏览器只记一次,再点取消)、评论与回复、阅读数。管理员可置顶,置顶的排在最前并带一枚「置顶」小标;博主在微语页就能直接驳回或删除自己的微语。
**版式**:顶部一块底图,可以是视频或封面,留空则用站点绿;下面「全部/文字/图集/音乐/视频」分类行吸附在顶栏下,翻到哪儿都能随时换;图片自动排成 1/2/3 列,点开是灯箱。视频在列表里一律缩成半行宽的一格,横屏竖屏都锁同一个比例(裁中间,完整画面点开才看),两条摆在一起一样大、播放键都在格子正中。没封面的会自己取一帧真画面顶着,不会缩成一小块黑。点播放键或画面会浮到全屏,按原始比例播完整画面,点空白处(或 Esc)退回列表。
**评论**:表单是一张白卡,压在评论列表的灰底上,「别人说过的」和「你要写的」一眼分开;游客的昵称/网址/邮箱并排一行。整条评论就是回复入口,点正文直接 @ 这位;管理员的通过/删除图标平时收着,指针落到那条评论才露出来。
**分享卡片**:纯文字微语里带外链,发布时抓一次标题、摘要、封面,生成一张可点击的卡片,正文里那行裸地址不再摆第二遍。老微语可在后台「微语墙」勾选补抓。
**加载与搜索**:滚到快见底自动接下一页,末尾先出「加载中」再落新内容;后台也可改用数字分页。顶栏放大镜开一个整屏搜索页,回车走普通跳转,不装 JS 也能用。
**夜景**:宽屏右下角两枚圆钮,上面切深色/浅色模式、下面回到顶部;手机上夜景键留在顶栏。没手动切过的访客跟随系统深色设置。
辅助文字的对比度、小按钮的点按热区、时间的时区排版都按移动端标准核过。想整体换个风格,改 `style.css` 里 `:root` 的三档圆角变量即可,不用逐条找。
---
## 三、配置项
配置页分六个标签页:**页面显示 / 功能开关 / 容量频率 / 媒体顶图 / 底部备案 / 微信助理**。
标签条用 Typecho 后台自带样式,没引外部样式表;所有字段始终在同一个表单里,任意标签下保存都会把六组一起存进去。
### 1. 页面显示
| 字段 | 默认 | 说明 |
| --- | --- | --- |
| 页面标题 | 微语 | 顶栏左侧,同时作为浏览器标题 |
| 页面描述 | 记录当下的碎碎念 | SEO description,也是封面下面那行右对齐的签名 |
| 每页微语条数 perPage | 20 | 1–100 |
| 列表翻页方式 listPaging | 加载更多 | 加载更多贴合手机信息流;数字分页可直接跳页 |
| 点赞列表最多显示人数 likeListLimit | 30 | 1–200 |
| 话题前缀 topicPrefix | `#` | 置空则话题不高亮、不生成搜索链接 |
| 时间格式 dateFormat | `Y-m-d H:i` | 一小时内的记录自动显示「xx 分钟前」 |
| 自定义头像 avatarMap | 空 | 每行一条「邮箱=图片地址」,给取不到头像的账号兜底 |
| 在线字体名称 fontName | 空 | 插件用它生成 `@font-face` |
| 字体地址 fontUrl | 空 | 字体文件或字体 CSS 地址;**两项一起填才加载** |
**插件不带任何字体文件**。默认字体栈是 `HarmonyOS Sans SC → 系统默认`,设备没装过鸿蒙字体就一直是回落状态;想让访客也看到,得自己配一个在线字体。
「字体地址」认两种:结尾是 `.woff2`/`.woff`/`.ttf`/`.otf` 的按字体文件处理,生成带 `font-display:swap` 的 `@font-face`(跨域要求服务端回 `Access-Control-Allow-Origin`);其他结尾原样外链成样式表,适合用按 `unicode-range` 分包好的地址。保存时非 http(s) 会被清空并提示,文件名空格自动转 `%20`,字体名只保留字母数字下划线、空格、点、连字符。
### 2. 功能开关(勾选即开启)
| 键 | 名称 | 默认 | 含义 |
| --- | --- | --- | --- |
| allowGuest | 允许游客发布微语 | 关 | 关则必须登录才能发,顶栏左侧显示登录图标 |
| guestNeedMail | 游客发布需填写邮箱 | 关 | 开启后游客发帖必须填合法邮箱(用于头像,不公开) |
| needReview | 非管理员发布需审核 | **开** | 管理员/博主发布永远免审 |
| allowComment | 开启评论与回复 | 开 | 关则隐藏评论区,每条下面的评论图标也一并去掉 |
| guestComment | 允许游客评论 | 开 | 关则仅登录用户可评论 |
| commentReview | 游客评论需审核 | 开 | 通过后显示在评论区并同步文章评论数 |
| commentNeedMail | 游客评论需填写邮箱 | 开 | 关闭时评论框不再要求邮箱 |
| allowLike | 开启点赞(同一指纹仅一次) | 开 | 再点一次为取消 |
| allowUpload | 允许本地上传图片/音乐/视频 | 开 | 关则只能用外链地址 |
| showViews | 显示阅读数 | 开 | 每条微语的浏览计数 |
| showEdited | 显示已编辑 | 开 | 关掉后前台不再给改过的微语标「已编辑」,修改时间照旧记录 |
| ajaxLoad | 列表无刷新加载 | **开** | 关掉后换分类、翻页都整页重载;`?m=12` 本来就是整页,不受影响 |
| showInFooter | 在文章页脚显示微语入口 | 关 | 通过 `Widget_Archive:footer` 钩子注入 |
| videoMuted | 视频默认静音 | 开 | 移动端内联自动播放必需 |
| videoInline | 强制内联播放 | 开 | 输出 `playsinline` / X5 参数 |
| allowShare | 正文链接自动生成分享卡片 | 开 | 取第一个外链抓标题/摘要/封面;卡片出来后那行裸地址不再显示 |
| weixin | 开启微信助理 | 关 | 见第五节 |
| notifyOnPublish | 新微语推送 WebHook 通知 | 关 | 配合下方 notifyUrl |
| autoUpdate | 后台自动检查新版本 | 关 | 每天一次静默比对仓库版本,**只提示不替换文件** |
| purgeOnDeactivate | 停用时一并删除数据表 | 关 | 开启后「停用插件」会 DROP 三张表 |
> 勾上「停用时一并删除数据表」前请先在 **设置 → 备份数据** 导出;
> 上传目录 `usr/uploads/LcMurmur/` 不会被自动删除,需手动清理。
### 3. 容量与频率
| 字段 | 默认 | 说明 |
| --- | --- | --- |
| maxImages | 9 | 单条最多图片数,1–9 |
| maxTextLength | 2000 | 正文最大字数 |
| maxCommentLength | 1000 | 评论最大字数 |
| postInterval | 30 | 游客发布间隔(秒),0 不限;微信助理不受限 |
| commentInterval | 15 | 游客评论间隔(秒),0 不限 |
| maxImageMb / maxAudioMb / maxVideoMb | 8 / 32 / 64 | 上传上限,实际还受 PHP `upload_max_filesize`、`post_max_size` 限制 |
| imageOpt | webp | `off` 关闭 / `compress` 只压缩 / `webp` 压缩并转 WebP。GIF 一律不动(重编码只剩一帧),PNG 转 WebP 保留透明;压不动或内存不够时存原图 |
| imageMaxEdge | 1600 | 最长边像素,超了等比缩小;填 0 只重编码不缩放 |
| imageQuality | 82 | 重编码质量,1–100 |
数值超范围时保存会被自动夹到合法区间,并在页面顶部提示修正结果。
### 4. 媒体顶图
| 字段 | 说明 |
| --- | --- |
| 顶部 MP4 视频地址 topVideo | 填写后顶部以该视频作底图,静音循环、不出播放条;留空改用封面头部。两种都锁 16:9 |
| 顶部封面图地址 topCover | 没有顶部视频时用作背景,留空则用绿渐变;跟视频同一档比例(铺不满时裁边) |
| 顶图封面地址 topPoster | 可选,首帧加载前显示 |
| 播放模式 videoPlayMode | `点击播放` / `刷新自动播放`(自动播放需静音,已默认静音);只管正文里的视频 |
| 播放结束画面 videoEndFrame | `第一帧` / `最后一帧`(顶部视频循环播放,不受影响) |
上传文件保存在 `usr/uploads/LcMurmur/年/月/`,文件名 `YmdHis-随机串.扩展名`。
扩展名白名单:图片 `jpg jpeg png gif webp bmp`,视频 `mp4 m4v mov webm`,音频 `mp3 m4a wav ogg flac aac`。
### 5. 底部备案
| 字段 | 说明 |
| --- | --- |
| ICP 备案号 icp | 填了才在底部显示,点击跳工信部备案查询站 |
| 公安备案号 policeIcp | 填了才显示,排在 ICP 后面,点击跳全国互联网安全管理服务平台 |
### 6. 微信助理
| 字段 | 说明 |
| --- | --- |
| wxToken | 与公众平台「服务器配置 → Token」一致 |
| wxAppid / wxSecret | 用于下载微信临时素材(发图/发视频时必需),可留空 |
| wxAllowUsers | 授权微信号 OpenID,逗号分隔(中文逗号、顿号、换行都认);**留空表示不限制** |
| notifyUrl | 可选 WebHook,新微语以 JSON POST 推送 |
### 7. 当前版本
只读一页,没有可填的字段:当前版本、仓库最新、更新源、上次检查、备份情况,以及最近一次在线更新的记录。
更新源写死在 `Update.php` 里(`https://gitee.com/lczld/LcMurmur` 的 `master` 分支),不在后台开放修改。
「检查更新」「立即更新」两个按钮在 **微语 → 微语墙** 页面顶部。
---
## 四、审核与数据
- 待审内容在 **后台 → 微语 → 待审核**,可单条操作,也可勾选后用「选中项」批量通过/驳回/删除/置顶;下半部分为待审评论,支持通过、退回待审、删除(连带清理其下回复)。
- **编辑**:微语墙每行都有「编辑」(编辑及以上可见),正文、图片、音乐、视频都能改,带哪一块就在表单上摆哪一块,没有的不画。图片勾「删除」即摘掉,新传的排在后面;音乐改歌名/歌手/封面/音频地址,视频改 MP4 地址、封面,以及这一条自己的「播放方式/结束画面」(不动「功能开关」里的全局设置)。想摘掉音乐或视频,把它的地址清空再保存,磁盘上的文件不动。开了「允许本地上传」,两块地址旁边各有一颗「上传并填入」按钮。状态、作者、点赞评论一概不动,改过的前台在时间旁标一枚「已编辑」,悬停显示修改时间;这枚记号可用「功能开关 → 显示已编辑」关掉(修改时间照旧记录)。
- 前台列表只展示 `approved`,驳回为 `hidden`(数据保留,可在「微语墙」按状态筛出后恢复)。
- 评论通过后同步写回所属微语的评论数。
- 数据表(前缀随站点):`typecho_LcMurmur` 微语主表(置顶时间存 `top`,0 为未置顶;老库第一次读列表自动补这一列)、`typecho_LcMurmurLikes` 点赞(按指纹唯一)、`typecho_LcMurmurComments` 评论与回复。
主题里加导航链接:
```php
微语
```
---
## 五、微信助理
### 1. 公众平台侧配置
1. 后台开启「开启微信助理」,填好 **wxToken**(自己起一串字母数字即可),保存一次。
2. 公众平台 → 设置与开发 → 基本配置 → 服务器配置:
- URL:`https://你的站点/index.php/murmur-api/weixin`(站点根已含 `index.php` 时可写成 `/murmur-api/weixin`)
- Token:与 wxToken 完全一致
- 消息加解密方式:**明文模式**
3. 提交后插件校验签名并回显 `echostr`,通过即接入成功。
4. 要直接发微信里的图片/视频(临时素材)需同时填 **wxAppid / wxSecret**;只发文字、语音、外链可留空。
签名校验对 GET 与 POST 均生效,未通过签名的请求一律 403。
### 2. 谁能发
`wxAllowUsers` 填 OpenID,多个逗号分隔;**留空 = 不限制**。白名单内成员同时拥有审核权限(可执行「通过/驳回」)。
这一栏只挑 OpenID 那串字符,怎么填都算数:中文逗号、顿号、分号、换行、不间断空格都能分隔,连微信回的整句「该微信号未获授权 (OpenID: xxx)」粘进去也能挑出来。一个 OpenID 都挑不出来时按「拒绝所有人」处理,不会悄悄退化成不限制。
拿自己的 OpenID:**给公众号发一句 `我的open`**,这条不受白名单限制,没被授权照样能问。被挡在外面时回复里也会带上对方 OpenID,单独占一行,方便完整复制。
### 3. 指令表
| 发送内容 | 效果 |
| --- | --- |
| 任意文字(不以指令开头) | 直接发布一条文字动态 |
| 图片 / 视频 | 发布对应动态,微信临时素材自动转存为外链 |
| 语音 | 取识别结果发布文字动态;识别失败则提示改发文字 |
| `音乐 歌名\|歌手\|音频链接` | 发布音乐动态,可再加封面:`歌名\|歌手\|封面\|音频链接` |
| `视频 封面图\|MP4链接` | 用外链发布视频动态,顺序不限,可只发 MP4 |
| `帮助` / `help` | 查看指令说明 |
| `待审` / `pending` | 列出最多 10 条待审微语及编号 |
| `通过 12` / `approve 12` | 通过第 12 条(并触发 WebHook 通知) |
| `驳回 12` / `deny 12` | 驳回第 12 条 |
| `列表` / `list` | 最新 5 条已发布微语 |
| `我的open` / `我是谁` | 回复本微信号 OpenID(**未被授权时也能问**) |
通过微信发布的动态作者显示为「微信助理」,来源标记 `weixin`,签名校验通过的回调免审核直接上墙。
它没有博主那套邮箱,头像用插件自带的一枚双气泡图标(`static/weixin.svg`),想换成自己的直接覆盖这个文件。
注意:`wxAllowUsers` 留空时任何人都能发,但**审核指令仍需后台管理员或白名单成员**。
### 4. WebHook 通知
勾选「新微语推送 WebHook 通知」并填 `notifyUrl`,每次发布或通过审核会 POST 一段 JSON:
```json
{
"action": "lc_murmur_new",
"id": 14,
"author": "微信助理",
"text": "正文前 200 字",
"url": "https://你的站点/index.php/murmur/?m=14"
}
```
`Content-Type: application/json`,超时 5 秒,失败静默(不影响发布)。
---
## 六、安全
- 后台面板与批量操作走 Typecho 原生 `Widget\Security::protect()`,需登录且具备 contributor/editor 权限(微语墙 contributor、待审核 editor)。
- 前台写接口带一次性令牌 `_` 与来源 `from`,令牌由站点 secret 派生;「删除/驳回微语」「评论审核」仅管理员可用。
- 「检查更新 / 立即更新」走同一个 action,链接带 `Widget\Security` 的令牌且服务端再验一次 `administrator`,面板上不出现这两个按钮的角色即便拼出地址也拿不到执行权。更新源是写死在代码里的常量,后台没有可填写的仓库地址,也就没有「填个地址让服务端去抓」这条攻击面;下载来的压缩包在解压前逐条校验条目名,`..`、绝对路径、反斜杠一律拒绝;替换用的临时目录建在 `usr/uploads/LcMurmur/_update/` 下而不是 `usr/plugins/`,避免 Typecho 把 `*.old` 当成第二个插件加载导致类名冲突。
- 正文入库前经 `Typecho\Common::removeXSS()`,输出时再次转义;外链只接受 http/https 且扩展名在白名单内。
- 分享卡片的目标地址在抓取前过一遍地址校验:只放公网 http(s),内网、本机、解析到私有网段的域名与本站地址一律拒绝,不做服务端请求转发。
- 页面底部那段 `window.LcMurmur={...}` 带着地址栏传来的搜索词,`json_encode` 除 `JSON_UNESCAPED_UNICODE|SLASHES` 外**必须带 `JSON_HEX_TAG|JSON_HEX_AMP`**,否则 `?mq=<…>` 能把这段脚本提前关掉;JS 侧用 `JSON.parse` 读回,转义不影响原词。
- 游客邮箱只用于取头像,不在前台展示。数字 QQ 邮箱(含 @vip.qq.com、@foxmail.com)走腾讯 `q1.qlogo.cn`,其余按 Gravatar 哈希走 `weavatar.com` 镜像;取不到时由 `onerror` 换成插件自带的 `static/avatar.svg`。占位图底色是**半透明**绿(`rgba(7,193,96,.12)`)——它挂在 `
` 上,SVG 里的 `prefers-color-scheme` 认不得顶栏那枚手动切的夜景开关,写死会在夜景下留一块亮绿。英文名 QQ 邮箱腾讯不提供反查,可在「自定义头像」里指定图片地址。
---
## 七、卸载
1. 后台 → 插件 → LcMurmur → 「停用」。默认**保留数据表**,便于再次启用。
2. 确认不再使用:勾选「停用时一并删除数据表」后再停用(会 DROP 三张表),或停用后手动删除 `usr/plugins/LcMurmur/` 与 `usr/uploads/LcMurmur/`(后者含上传文件和在线更新的备份)。
3. 停用会同时移除路由、后台菜单与面板,不残留链接。
---
## 八、常见问题
**Q:保存配置后设置页打不开(500)?**
A:1.0.0 已对落库字段做白名单过滤,不再出现该问题;若从旧版手工写入过额外字段,重新「停用 → 启用」一次即可自动清理。
**Q:升级后新加的功能开关是空的?**
A:开关只记「勾了哪些」,老库里没有新键,所以新版自带的开关在旧站第一次是未勾选状态。到 功能开关 勾上保存即可,新装站点不受影响。
**Q:游客发了但前台看不到?**
A:多半开启了「非管理员发布需审核」,请到 后台 → 微语 → 待审核 处理。
**Q:视频在手机上点开变成全屏播放器 / 不自动播放?**
A:保持「视频默认静音」「强制内联播放」开启;「刷新自动播放」也依赖静音才生效。
**Q:上传图片提示 413 / 上传失败?**
A:PHP 的 `upload_max_filesize`、`post_max_size` 小于配置里的上限,调大即可,或改用外链图片。
**Q:微信接入一直提示「微信助理未开启」?**
A:先保存一次配置(开关才会写入数据库),并确认公众平台使用明文模式、Token 与 wxToken 一致。
**Q:微信回「该微信号未获授权」?**
A:签名过了、服务器也认出了你的微信号,只是后台那一栏的 OpenID 没对上。清空那一栏保存 → 发一句 `我的open` → 把回的那串原样填回去。
**Q:点「立即更新」提示「下载到的不是 zip 安装包(HTTP 200,开头是「