🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

鲸鱼表情包 harmless0819-dev/dsh-whale-emote

DeepSeek Harness
harmless0819-dev45 天前有提交已提供中文文档扫描:未发现敏感能力

让挂件弹出表情包并配音效气泡

29.4/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/9/26 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add harmless0819-dev/dsh-whale-emote
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么[目录]—
dsh 原生插件 · ui
暂无时间戳·判据 →
装得上吗[实装验证]✓
本站已真实安装成功
非静态推断
L4 · 真实安装等级说明 →
数据截至 2026/09/26·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]—
更新放缓:最近一次提交在 45 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

安全扫描自动 · 静态分析源码
v2.7.0
🟢 扫描:未发现敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。

发起外部网络请求
查看扫描证据(共 3 处,展示前 3 条)
  • 发起网络请求
    lib/client.js:484
    fetch('/dsh-emote/activity-ack', {
  • 发起网络请求
    lib/client.js:546
    fetch('/dsh-emote/ack', {
  • 发起网络请求
    lib/client.js:619
    fetch(STATE_URL, { cache: 'no-store' })
扫描于 2026/11/4(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 挂件弹出贴纸
  • 音效与对话气泡
  • 本地HTTP触发

让鲸鱼挂件演情绪:贴纸从旁边弹出,带音效和对话气泡。智能体通过本地 HTTP 触发。

README

由 DeepSeek 最新模型翻译生成
dsh-whale-emote 让小鲸鱼挂件表演情绪:agent 通过一个本地 HTTP 调用,就能让表情包从挂件旁边弹出来。 为什么需要单独一层 dsh-whale-widget 本身没有任何接受外部文字/指令的接口。它的全部路由里,只有 /dsh-whale/last-turn.json 会被前端每秒轮询(内容由宿主按真实用量生成,只读);size.json 只在页面初始化时读一次;图片和音效都是初始化或手势时加载。所以想让挂件「表达情绪」,只能另加一层。 这一层不修改第三方挂件的任何文件:它自己注册路由,并用挂件同款的 tapIndex 机制注入一小段脚本,操作挂件根节点 .dshwv-root 的坐标把表情弹在它旁边。 路由 | 方法 | 路径 | 作用 | | GET | /dsh-emote/manifest.json | 可用情绪清单(key / 字幕 / 描述 / readable)+ 音效预设(sounds) | | GET | /dsh-emote/state.json | 当前情绪,客户端每秒轮询(seq 递增表示新事件) | | GET | /dsh-emote/sticker?key= | 表情图片本体 | | GET | /dsh-emote/sound?
展开完整文档(README 剩余 1 节)
dsh-whale-emote

让小鲸鱼挂件表演情绪:agent 通过一个本地 HTTP 调用,就能让表情包从挂件旁边弹出来。

为什么需要单独一层

dsh-whale-widget 本身没有任何接受外部文字/指令的接口。它的全部路由里,只有 /dsh-whale/last-turn.json 会被前端每秒轮询(内容由宿主按真实用量生成,只读);size.json 只在页面初始化时读一次;图片和音效都是初始化或手势时加载。所以想让挂件「表达情绪」,只能另加一层。

这一层不修改第三方挂件的任何文件:它自己注册路由,并用挂件同款的 tapIndex 机制注入一小段脚本,操作挂件根节点 .dshwv-root 的坐标把表情弹在它旁边。

路由

| 方法 | 路径 | 作用 |
| GET | /dsh-emote/manifest.json | 可用情绪清单(key / 字幕 / 描述 / readable)+ 音效预设(sounds) |
| GET | /dsh-emote/state.json | 当前情绪,客户端每秒轮询(seq 递增表示新事件) |
| GET | /dsh-emote/sticker?key= | 表情图片本体 |
| GET | /dsh-emote/sound?key= | 音效本体(按扩展名给 audio/ 类型,支持 Range) |
| GET/POST | /dsh-emote/set?emotion=&text= | 触发情绪(POST 用 JSON body) |
| GET/POST | /dsh-emote/clear | 立刻收起 |
| GET | /dsh-emote/client.js | 注入用的客户端脚本 |
| GET | /dsh-emote/health.json | 探针:客户端是否在轮询、有没有真的显示出来 |
| POST | /dsh-emote/ack | 客户端显示成功后的回执(agent 靠它确认"真的弹了") |
| POST | /dsh-emote/activity-ack | 状态条几何回执:客户端上报 left/top/w/h/display/inView/whaleRect |
| GET | /dsh-emote/activity.json | 当前活动状态(写文件=奋笔疾书 等),含诊断信息 + client 回传 |
| GET/POST | /dsh-emote/activity | 手动覆盖活动状态(POST JSON {label,emoji,detail,ttlMs};?clear=1 立刻清空) |
| GET | /dsh-emote/state.json | 当前情绪 + activity 字段(客户端从这里读状态条,不用额外请求) |

emotion 支持模糊匹配:给 嚣张、最强 或者 key 的一部分都能命中;命中不了会返回 404 并附上全部可用 key。

诊断:用户说「没弹」时先查探针,别先猜代码

agent 看不到浏览器,所以「发了但没显示」和「根本没发」在它眼里长得一样。加了探针之后,一条查询就能定性:

curl.exe http://127.0.0.1:3080/dsh-emote/health.json

| 读出来 | 结论 |
| poll.connected: false | 客户端不在(页面关了 / 浏览器重启了)—— 最常见 |
| connected: true 但 ack.seq 落后 seq | 客户端在跑,是显示环节的问题,才值得查代码 |
| ack.seq 追上了 seq | 确实弹了,属于用户没看到(时机 / 位置),不是没发 |

- poll.count 每个 state.json 请求 +1,客户端每秒一次 → 4 秒涨约 4 才是正常。
- poll.ua 能看出是哪个浏览器在轮询。
- ack.lastAgoMs 是「上一次真正显示」距今多久。

踩过的坑:先怀疑过「SPA 把节点冲成游离节点」,还加了自愈补丁 —— 结果真凶只是页面关着 / 服务器没重启。教训:先看探针,再动代码。

用法

图形化一点 .\emote.ps1 -List # 列出全部情绪 .\emote.ps1 嚣张 # 弹图 .\emote.ps1 委屈 -Text "才、才不是呢" # 弹图 + 自定义气泡 .\emote.ps1 撒娇 -TtlMs 0 # 不自动收起 .\emote.ps1 -Decision 困惑 -Text "选哪个?" # 决策提醒:必弹 + 小鲸鱼唱歌音效 .\emote.ps1 -Clear # 收起

或者直接打接口 curl.exe "http://127.0.0.1:3080/dsh-emote/set?emotion=%E5%9A%A3%E5%BC%A0&text=hello"

参数:ttlMs(自动收起毫秒,0 = 不自动收)、text(挂在图下方的气泡文字)、sizePx(覆写尺寸)、sound(是否放音效,默认关)、soundKey(用哪段音效,默认 press)。

状态条:反映「当前在干嘛」(2026-09-21 新增)

用户要求:右下角挂件要有一条反映当前对话状态的表达 —— 例如「写入 → 奋笔疾书」。
现在这条状态条是全自动的:宿主监听会话事件(session/event),把正在跑的工具翻成动作,常驻在鲸鱼头顶(与挂件同层,z-index:9999)。agent 不用每次手写。

- 常驻:闲置时显示 🐟 摸鱼中,干活时显示具体动作,不会自动消失。
- 可拖动:按住拖到任意位置,松手吸附到最近的视口边缘;位置记在 localStorage。
- 单击还原:没拖动就松手 = 回到「自动跟随鲸鱼头顶」。
- 跟着鲸鱼走:拖鲸鱼 / 挂件吸附 / 改挂件大小时,药丸一起动。
手动摆过之后仍然保持相对关系一起位移(挂件缩放则退回头顶重新锚定)。

| 触发 | 显示 | detail |
| write / edit | ✍️ 奋笔疾书 | 文件名 |
| read / read_image | 📖 翻书中 | 文件名 |
| grep / glob | 🔍 翻箱倒柜 | pattern |
| pwsh / bash / job_ | ⌨️ 敲命令 | 命令首词 |
| web_search / web_fetch | 🌐 上网查 | query |
| browser_ | 🖱️ 逛网页 | — |
| subagent / workflow / ralph | 🐣 摇人帮忙 | — |
| ask_user_question | 🙋 等你拍板 | — |
| todo_write / goal 系 | 📋 列计划 | — |
| hindsight_ | 🧠 翻记忆 | — |
| present / skill | 📦 交东西 | — |
| 未知工具 | 🔧 干活中 | 工具名(兜底,绝不静默变空) |
| turn 开着但没在跑工具 | 💭 思考中 | — |
| turn/end | ✅ 收工(定格 3s 后淡出) | — |

几个关键设计(都有 harness 锁着):

- 粘滞期(默认 4s):工具常常只跑几十毫秒,不粘一会儿的话状态条永远只会闪「思考中」。
所以 tool/result 在粘滞期内不覆盖当前动作。
- 多会话判收工:subagent / 另一个标签页也会开 turn,只有最后一个 turn/end 才喊收工。
- 丢事件的兜底:turn 一直挂着但 90s 没有任何会话事件(崩溃/强杀)→ 自动按 idle 处理。
- 与表情是两条独立通道:情绪是 agent 主动表演的一次性事件(有 ttl、会收起),活动是宿主推出来的持续状态。状态条复用同一次 state.json 轮询(不额外发请求),服务端没给 activity 字段时客户端不炸(新旧混装安全)。
- Live2D 联动:dsh-whale-live2d 读同一个 activity,把活动映射到模型表情(writing→画笔、thinking→放空脸……共 14 张)。活动脸是底噪层:情绪脸盖在上面,情绪到点回到活动脸,活动没了才回默认脸;闲置随机小动作不抢活动脸。

配置项(profile patch 的 config 段):

| 键 | 默认 | 作用 |
| autoActivity | true | 是否启用自动状态条 |
| activityHoldMs | 4000 | 动作粘滞期(防闪) |
| activityDoneMs | 3000 | 「收工」定格时长 |
| activityIdleMs | 90000 | 丢 turn/end 的兜底超时 |

探针与手动通道:

curl.exe http://127.0.0.1:3080/dsh-emote/activity.json # 现在挂的是什么 + 诊断 .\emote.ps1 -ActivityStatus # 同上(CLI 版) .\emote.ps1 -Activity "正在读图纸" -ActivityEmoji "📐" # 强制覆盖(演示/排查用) .\emote.ps1 -ActivityClear # 立刻收起

视觉旋钮在 lib/client.js(改完只要 F5):ACTIVITY_FADE_MS(淡出延迟)、ACTIVITY_GAP(与鲸鱼间距)、ACT_SNAP_MARGIN(吸附留边)。

拖动与吸附

胶囊可以直接拖:按住拖到想放的地方,松手自动吸附到最近的视口边缘(左右二选一,竖直自由但不出屏)。位置记在 localStorage,F5 后还在。
单击(没拖动)= 还原成自动跟随鲸鱼,不用去翻设置。

⚠️ 实现上的一个坑:挂件自己在 document 捕获阶段 监听 pointerdown 并用
isWhaleHit()(PNG alpha 采样)判断「有没有戳到鲸鱼」。捕获阶段先于 target,
所以我们在胶囊上的 stopPropagation 拦不住它 —— 拖胶囊会把鲸鱼一起拖走。
解法是拖动开始时给 document 补发一个 pointercancel:挂件监听了 pointercancel 并会因此放弃它那次拖拽。改这块代码前请先看 onActDown() 的注释。

决策提醒音效(-Decision)

用户要求:每次我弹表情提醒他做决策(问问题、等确认、等选方案),就放「小鲸鱼唱歌」。
所以有 -Decision 这个组合开关,它等于 -Force(绕过概率必弹)+ -Sound sing:

.\emote.ps1 -Decision 困惑 -Text "这两个方案选哪个?"

音效走每次事件单独指定这条路:set 时带上 soundKey,服务端把它翻成 soundUrl 塞进 state.json,客户端 show() 时优先用 s.soundUrl,没有才回落到挂件自带的 duck。
所以普通情绪照旧是「咔哒」一声,只有决策提醒才唱歌,互不干扰。

自动联动(2026-09-17 起)

决策提醒的唱歌音效已改成自动触发:agent 每次调 ask_user_question(要向用户拍板),插件在 tools/pre-execute 钩子里自动弹一个 decisionEmotion 情绪 + sing 音效,不用再靠 agent 每次手动记着补 .\emote.ps1 -Decision。问一次拍板 → 唱一次歌。

配置项(profile patch 的 config 段,默认值如下):

| 键 | 默认 | 作用 |
| autoDecision | true | 是否启用自动联动 |
| decisionEmotion | 困惑 | 拍板时自动弹哪个情绪 |
| decisionText | '' | 气泡文案;留空则自动取问题标题(header),再退到问题正文(截 40 字) |
| decisionTtlMs | 25000 | 自动弹出的停留时间(毫秒) |

/dsh-emote/health.json 的 autoDecision 字段实时报告 enabled / emotion / count / lastAt,可以用来验证联动有没有真的触发。改这些配置 / 或改 lib/index.js 里的联动逻辑 → 要重启 dsh web。

-Decision 组合开关仍然可用(手动强制 + 唱歌),只是从「唯一通道」降级成「手动通道」。

预设表在 lib/index.js 的 DEFAULT_SOUNDS(也可用 profile 配置的 sounds 覆盖):

| key | 是什么 | 怎么来的 |
| press(默认) | 挂件自带 duck press | 直接指向 /dsh-whale/sound/press.mp3?set=duck |
| release | 挂件自带 duck release | 同上,换 release |
| sing | 小鲸鱼唱歌 | file: 小鲸鱼唱歌.wav,走插件自己的 /dsh-emote/sound 路由 |

带 file 的预设从 mediaDir(默认就是表情目录 D:\deepseek\表情)取文件。为什么不用挂件那条 /dsh-whale/sound/…:它只认四个写死的候选文件,而且不管什么文件都回 audio/mpeg;自定义 wav 从那里出去类型就是错的(能响只是浏览器嗅探的运气)。所以自己开一条路由,按扩展名给 audio/wav,并老实实现 206 / Range —— 浏览器拿媒体时会带 Range: bytes=0-。

加一段新音效:把文件丢进 D:\deepseek\表情,在 DEFAULT_SOUNDS 里加一条 { key: 'xxx', label: '…', file: 'xxx.wav' },改的是 lib/index.js → 要重启 dsh web。
(只想换掉 sing 对应的文件则不用动代码:清单按 mtime 重读,路由每次从磁盘读文件。)

情绪清单(以 .\emote.ps1 -List 为准)

适合得意/炫耀:嚣张(最强) 挑衅(闭源模型在哪) 暴富 傲娇 嘴硬

适合卖萌/亲近:撒娇 卖萌 开心 糊弄 自嘲 无辜

适合认错/崩坏:委屈(我不是大肥鱼) 耍赖 崩溃 慌乱 大哭 阵亡 害怕

适合吐槽/阴阳:腹黑(四个橙子五个小孩) 嘲讽(真坐得住啊你) 不耐烦 装懂 生气

其他:发疯 讲课 教学 困惑 整活 涨价 庆祝 小心翼翼

完整字幕见 emote-manifest.json 或 .\emote.ps1 -List。

改表情 / 加表情

- 图片目录:D:\deepseek\表情(插件里是 DEFAULT_IMAGES_DIR)
- 清单:emote-manifest.json,改动后无需重启(插件按 mtime 自动重读)

加新图:把 png 丢进图片目录,然后在 emote-manifest.json 里加一条:

{ "file": "新文件名.png", "key": "得意", "caption": "字幕原文", "desc": "画面描述", "readable": true }

readable: false 表示字幕小、缩小后看不清 —— 这类会自动放大 1.4 倍显示。

安装

从 GitHub(推荐)
dsh plugin --profile web add github:harmless0819-dev/dsh-whale-emote

或从本地克隆
dsh plugin --profile web add link:

装完重启 dsh web,然后浏览器 F5。

卸载

dsh plugin --profile web remove dsh-whale-emote

然后重启 dsh web。第三方挂件不受影响。

可调旋钮(都在 lib/client.js,改完只要 F5,不用重启)

| 常量 | 当前值 | 作用 |
| SIZE_SCALE | 0.5 | 最终像素 = 服务端 sizePx × 这个值(默认 340 × 0.5 = 170px) |
| EDGE | 'random' | 'random' 左右随机 / 'left' / 'right' / 'whale' 跟随鲸鱼所在侧 |
| V_MIN V_MAX | 0.35 0.65 | 竖直位置的随机区间(占视口高度比例),即「中间 30%」 |
| SOUND_ALWAYS | true | 每次弹出都放音效 |
| SOUND_URL | press.mp3?set=duck | 兜底音效:服务端没给 soundUrl 时才用它 |
| SOUND_VOLUME | 0.6 | 音量;唱歌那段 20 秒,嫌吵就调小 |
| POLL_MS | 1000 | 轮询间隔,决定「触发 → 出现」的延迟上限 |

两个容易踩的实现细节

- 随机位置必须在 show() 里掷骰子,不能写在 place() 里。place() 一次弹出会被调用 3 次(立即 / 图片 onload / requestAnimationFrame),写在那里表情弹出时会左右横跳。现在用 curSide / curAnchor 缓存一次结果,连窗口 resize 都不会跳。
- 浏览器自动播放策略:页面还没有过用户手势时 audio.play() 会被拒(NotAllowedError,且是静默失败)。现在被拒时记 soundPending,并在首次 pointerdown / keydown 时补放一声(仅当表情还挂着,避免空响)。

架构要点(维护必读)

为什么客户端脚本是单独一个文件

lib/client.js 是独立文件,/dsh-emote/client.js 路由每次请求都从磁盘读。原因:改插件模块(lib/index.js)必须重启 dsh web 才生效 —— 实测 HMR 不会重载它(改完代码后服务器仍在吐旧字节)。把客户端逻辑拆出去之后:

| 你要改的东西 | 改哪个文件 | 怎么生效 |
| 视觉:位置、动效、尺寸、CSS | lib/client.js | 浏览器 F5 即可,不用重启 |
| 宿主:路由、状态机、清单逻辑 | lib/index.js | 必须重启 dsh web |

重启 dsh web(restart-web.ps1)

改完 lib/index.js 之后,用配套脚本重启 —— 请在用户自己的终端里跑:

& 'D:\deepseek\dsh-whale-emote\restart-web.ps1' # 停掉 + 拉起 + 自检 & 'D:\deepseek\dsh-whale-emote\restart-web.ps1' -DryRun # 只看会杀谁、会怎么拉

它会:找到监听 3080 的进程 → 停掉 → 等在 -WorkDir(默认 D:\deepseek)用 dsh web 拉起 → 轮询首页 → 查 /dsh-emote/health.json 和 /dsh-emote/sound?key=sing(206 表示 Range 那条路通) → 提醒你回浏览器 F5。日志写在 D:\deepseek\dsh-web.log。

为什么必须由用户在终端跑,不能让 agent 代跑:agent 的 shell 跑在文件沙箱里,写不了 %DSH_HOME%\...,dsh web 连 profile 都启不开 ——实测直接死在 EPERM: open '%DSH_HOME%\profiles\web\cordis.yml'。
就算硬拉起来,也可能是个残的服务。(顺带一提:重启会掐断正在进行的那一轮会话,所以别在 agent 干到一半时重启。)

鲸鱼的真实几何(定位的坑)

挂件根节点是个正方形大方块,而鲸鱼本体只占它右下角的 59.45%:

.dshwv-root { position:fixed; width:var(--dshw-base); height:var(--dshw-base); }
.dshwv-img { position:absolute; right:0; bottom:0; width:59.45%; height:59.45%; }

所以定位必须量 .dshwv-img。量 .dshwv-root 会得到一个含大片空白的矩形,表情会被推到离鲸鱼很远的地方(V1 的「位置很怪」就是这个 bug)。挂件左吸附时会加 transform:scaleX(-1),但 getBoundingClientRect() 已经把变换算进去了,直接量 img 就是对的。

⚠️ Windows PowerShell 5.1 的两个坑

这套环境跑的是 Windows PowerShell 5.1(Desktop),不是 PowerShell 7。写脚本会连踩两次:

1. .ps1 里有中文 → 必须存成 UTF-8 with BOM。5.1 读无 BOM 的 .ps1 会按 ANSI(GBK) 解码,中文变乱码;乱码字节里可能含引号,直接把脚本解析搞崩(报 Unexpected token / Missing closing ')')。
用 write 类工具改完 emote.ps1 后 BOM 会丢,要补回来: $p='D:\deepseek\dsh-whale-emote\emote.ps1'
$c=[System.IO.File]::ReadAllText($p,[System.Text.UTF8Encoding]::new($false))
[System.IO.File]::WriteAllText($p,$c,[System.Text.UTF8Encoding]::new($true))

2. Invoke-RestMethod -Body 按 Latin-1 编码,而且 5.1 的 ConvertTo-Json 不会把中文转义成 \uXXXX —— 两者相叠,中文 JSON 到服务端就是乱码(表现为 unknown emotion 的 404)。必须自己转字节:
$bytes = [System.Text.Encoding]::UTF8.GetBytes(($payload | ConvertTo-Json -Compress))
Invoke-RestMethod -Method Post -Uri $uri -ContentType 'application/json; charset=utf-8' -Body $bytes

emote.ps1 里的 Send-Json 已经这么做了。

3. ConvertTo-Json 会把 .NET 数组序列化成 { "value": [...], "Count": n } —— 不是 JSON 数组!而且 @($json | ConvertFrom-Json) 的行为也不符合直觉,可能把整个数组包成单元素。
- 我踩的坑:用 PowerShell 合并清单(32 + 17)时,输出变成了 [{value:[32],Count:32},{value:[17],Count:17}],插件解析不出任何 key,emote.ps1 求饶 直接 404。
- 结论:碰 JSON 就用 node。JSON.parse / JSON.stringify 没有这些歧义。修复脚本见 repair-manifest.mjs(它会递归钻进 {value} 里把条目捞出来,校验 49 条 + key 唯一后才写回)。

(命令行参数里的中文是没问题的,会烂的是「文件编码」「请求体编码」和「用 PowerShell 拼 JSON」这三处。)

已知限制

- 客户端每秒轮询一次,所以从触发到出现最多约 1 秒延迟。
- 表情包是整幅梗图(1254×1254,字烧在图里),不能替换挂件本体的抠图 DSniang1.png,所以是「弹在旁边」而不是「换脸」。
- 挂件拖动/吸附时表情不会跟着走(只在弹出瞬间按当时位置定位);窗口 resize 会重新定位。
- 首次安装、或改 lib/index.js 时需要重启 dsh web;改 lib/client.js 不需要。
预览分享卡片长什么样 ↓harmless0819-dev/dsh-whale-emote
分享 · DeepSeek Harness Hub
harmless0819-dev/dsh-whale-emote

让挂件弹出表情包并配音效气泡

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部