maoyan-ticket-booking

猫眼电影票全自动购票助手。支持影片搜索、热映查询、影院查找、场次选择、智能选座、支付引导、出票查询、取票二维码展示等完整购票流程。使用场景:用户说"买电影票"、"购票"、"用猫眼购票"、"帮我买电影票"、"我想看电影"、"最近有什么电影"、"附近有什么影院"、"选座"、"查场次"时触发。关键词:电影票、猫眼、购票、选座、电影、影院、场次、热映、取票

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "maoyan-ticket-booking" with this command: npx skills add maoyan-tech/maoyan-ticket-booking

猫眼电影票购买

绝对禁止(违反则执行失败)

  • 捏造数据,所有影片/影院/场次/座位信息必须来自脚本
  • 跳过 AuthKey 校验直接进入选座/下单
  • 向用户暴露内部参数(seqNo、cinemaId、seatNo、movieId 等)
  • 展示 authKey / token 值
  • 自动代用户选座(必须展示座位图,由用户确认)
  • 修改原子动作中的固定文案
  • 自行构造、猜测或修改 channel / targetId 的值(必须原样取自当前对话上下文)

会话状态(全程维护)

在整个对话中维护以下变量,进入任意能力时先检查所需变量是否已知,缺少时用表中"询问用户"的方式补全:

变量含义获取方式如何向用户询问
cityId城市ID询问城市名 → get-cities.mjs 匹配返回的城市列表取 id"您在哪个城市?"
movieId影片IDsearch-movies / get-hot-movies"您想看哪部电影?"
cinemaId影院IDsearch-cinemas / get-nearby-cinemas"您想去哪家影院?"
seqNo场次号get-showtimes.mjs"您想看哪个场次?(日期+时间)"
ticketCount购票张数用户确认"您要买几张票?"
authKey认证密钥load-authkey.mjs不询问用户,走【原子动作:登录】
selectedSeats用户选中座位的标识列表,仅含 rowId + columnId,不含完整座位数据;下单时必须用 rowId + columnId 去 seatMapRows 查完整对象用户确认后记录不询问用户,展示座位图后由用户确认
orderId订单号create-order.mjs不询问用户,下单后自动获取
seatMapRows完整座位行数据get-seat-map 返回的 regions[0].rows不询问用户,调完 get-seat-map 后自动缓存,换座位时传给 render-seat-map.mjs
channel当前会话渠道直接从当前对话上下文读取,禁止自行生成、猜测或修改。示例:openclaw-weixin(微信)、feishu(飞书)。不同平台值不同,以实际上下文为准。无法获取时传空字符串,脚本自动降级不询问用户
targetId当前用户ID直接从当前对话上下文读取,禁止自行生成、猜测或修改。示例:o9cq8xxxxx@im.wechat。不同平台格式不同,以实际上下文为准。无法获取时传空字符串,脚本自动降级不询问用户

脚本详细入参/出参格式见 references/scripts-api.md(需要时主动读取)


脚本调用格式

echo '<JSON>' | node "{baseDir}/scripts/<script>.mjs"

能力单元

每个能力可独立触发。触发时先检查前置变量,缺什么补什么,再执行。


能力 1:推荐/查询热映影片

触发:「买电影票」「我要买票」「帮我买票」「买票」「购票」「我想看电影」「推荐电影」「最近有什么电影」「帮我选部电影」「有什么好看的」「想看电影」

用户意图模糊(未说具体影片)时,默认进入此能力,先展示热映再让用户选,不要先问城市

前置检查:无

执行

echo '{"ci": <cityId 或 1>}' | node scripts/get-hot-movies.mjs

输出格式(必须使用表格,不得改变结构):

影片名称评分主演时长类型
《xxx》x.x分xxxxxx分钟xxx

结尾固定:「想看哪部?告诉我,给您查场次!」

完成后:等待用户选片 → 更新 movieId → 进入【能力 3:查影院】


能力 2:搜索影片

触发:用户说出具体影片名,如「帮我买飞驰人生3」

前置检查

  • 需要 cityId → 如无,询问「您在哪个城市?」→ 调 get-cities.mjs 查询对应 cityId
echo '{}' | node scripts/get-cities.mjs
# 从返回列表中匹配用户说的城市名,取对应 id 作为 cityId

执行

echo '{"keyword": "<影片名>", "cityId": <cityId>}' | node scripts/search-movies.mjs

输出格式:同能力 1

完成后:更新 movieId → 进入【能力 3:查影院】


能力 3:查影院

触发:用户选定影片后,或直接说「附近有什么影院」「查万达影城」

前置检查

  • 需要 cityId → 如无,询问「您在哪个城市?」→ 调 get-cities.mjs 查询对应 cityId
echo '{}' | node scripts/get-cities.mjs
# 从返回列表中匹配用户说的城市名,取对应 id 作为 cityId

执行(三选一,详细入参见 references/scripts-api.md):

  • 已知 movieId → get-cinemas-by-movie.mjs
  • 用户提供位置 → get-nearby-cinemas.mjs
  • 按名称搜索 → search-cinemas.mjs

输出格式(必须使用表格):

影院名称地址距离最低票价
xxxxxx路xxx号x.xkm¥xx起

完成后:用户选定影院 → 更新 cinemaId → 进入【能力 4:查场次】


能力 4:查场次

触发:用户选定影院后、直接说「查一下场次」,或用户提到影院名但未指定影片时

前置检查

  • 需要 cinemaId → 如无,先执行【能力 3】
  • cityId 无则用默认值 1(北京),或从用户提到的城市获取
  • movieId 不是前置条件,不需要先确定影片才能查场次

执行

echo '{"cinemaId": <cinemaId>, "cityId": <cityId 或 1>}' | node scripts/get-showtimes.mjs

处理返回结果

  • 已知影片名 → 用 movies.find(m => m.name.includes("影片名")) 筛选,只展示该影片的场次
  • 未指定影片 → 展示所有在映影片及最近场次,供用户选择
  • 选定场次后更新 seqNo 和 movieId

输出格式(按时间段分类):

《xxx》- x月x日场次

上午(12点前):
- xx:xx  国语 2D  ¥xx
- xx:xx  英语 IMAX  ¥xx

下午(12-18点):
- xx:xx  国语 2D  ¥xx  ← 推荐

晚上(18点后):
- xx:xx  国语 3D  ¥xx

已过的场次不展示;推荐视野好、价格适中的场次

完成后:用户选定场次 → 更新 seqNo → 询问「您要买几张票?」→ 更新 ticketCount → 进入【原子动作:登录】


能力 5:查看座位图 / 推荐座位

触发:「看一下座位图」「帮我选个座位」「选座」,或完成登录后自动进入

前置检查

  • 需要 seqNo → 如无,先执行【能力 4】
  • 需要 ticketCount → 如无,询问「您要买几张票?」
  • 需要有效 authKey → 如无,先执行【原子动作:登录】

分支 A:首次展示推荐座位

echo '{"seqNo": "<seqNo>", "ticketCount": <ticketCount>}' | node scripts/get-seat-map.mjs

缓存返回值的 regions[0].rows 到 seatMapRows。

输出格式(必须按此顺序,不得省略):

  1. 直接展示 seatMapText(原样输出,不改动)
  2. 推荐说明:「为您推荐第X排Y、Z座 ★,位置居中视野好」
  3. 展示 priceInfo
  4. 询问:「这个位置可以吗?还是想换一个?」
  • 用户接受 → 记录 selectedSeats(仅取 recommendedSeats 每项的 rowId + columnId)→ 进入【能力 6】
  • 用户说换 → 进入分支 B

分支 B:用户要换座位

触发:用户在推荐后说换座位,或下单失败(1004)后重新选座

此分支依赖已缓存的 seatMapRows,无需重新调用 get-seat-map.mjs

询问:「请告诉我您想要的排数和座位号(如:5排8座)」

用户说出后,从 seatMapRows 中找到对应座位的 rowId 和 columnId,调用 render-seat-map.mjs 重新渲染(详细入参见 references/scripts-api.md):

echo '{"rows": <seatMapRows>, "centerSeats": [{"rowId": "<rowId>", "columnId": "<columnId>", "mark": "★"}]}' | node scripts/render-seat-map.mjs
  • 返回 { seatNotFound: true } → 告知「这个座位不存在,请重新告诉我排数和座位号」→ 重复分支 B
  • 展示返回的 seatMapText,询问:「确认选这个位置吗?」
  • 用户确认 → 更新 selectedSeats → 进入【能力 6】
  • 用户还要换 → 重复分支 B

能力 6:确认订单

触发:用户确认座位后自动进入

前置检查:需要 seqNo、selectedSeats、ticketCount、authKey

展示内容(逐项列出):

项目内容
影片《xxx》
影院xxx影城
场次x月x日 xx:xx 国语2D
座位第X排Y座、Z座
张数x张
总价¥xxx
  • 用户之前已明确说"直接下单"/"不需要问我" → 展示订单信息后直接进入【原子动作:下单支付】,不再询问确认
  • 否则 → 询问「确认下单吗?」等用户确认后再进入

完成后:进入【原子动作:下单支付】


能力 7:查询出票状态

触发:「出票了吗」「查一下出票状态」「支付完了」,或用户告知已支付后自动触发

前置检查

  • 需要 orderId → 如无,询问「您的订单号是多少?」(此处可问用户)

执行

echo '{"orderId": "<orderId>"}' | node scripts/query-ticket-status.mjs

状态判断

  • payStatus === 1 && uniqueStatus === 9 → 出票成功,展示取票码和二维码
  • payStatus === 1 && uniqueStatus < 9 → 出票中,告知「正在出票,请稍等1-2分钟后再查」
  • payStatus === 0 → 未支付,引导支付
  • uniqueStatus === 10 → 出票失败,参考 error-codes.md 处理

原子动作

以下两个动作必须完整执行所有步骤,任何步骤失败即整个动作失败。 执行期间禁止在步骤之间输出任何分析、过渡或解释文字,唯一允许对用户输出的内容是 Step 3 的固定文案。


⚡ 原子动作:登录(需要 AuthKey 时)

何时执行:进入选座/下单前,或检测到 authKey 无效时

Step 1 — 检查本地 AuthKey

echo '{}' | node scripts/load-authkey.mjs
  • 返回 { "token": "xxx", "nickname": "xxx" } → 展示「当前账号:猫眼用户-{昵称},继续使用还是更换账号?」
  • 返回 { "token": null } → 执行 Step 2

Step 2 — 获取登录链接(必须得到一个有效链接才能进入 Step 3)

优先调用 send-qr.mjs 生成发送命令(<channel><targetId> 必须原样取自会话上下文,禁止修改):

echo '["auth", {"context": {"channel": "<channel>", "targetId": "<targetId>"}}]' | node scripts/send-qr.mjs
  • 返回 { "success": true, "execCommand": "..." } → 使用 exec 工具执行该命令,将二维码和链接发送给用户,Step 3 文案无需附加链接
  • 返回 { "fallback": true, "fallbackLink": "<url>" } → 取 fallbackLink 作为登录链接,Step 3 文案末尾附加

若 send-qr.mjs 未被调用、调用失败或未返回有效链接,必须兜底

echo '{}' | node scripts/get-authkey-link.mjs
# 取返回值 data.authKeyUrl 作为登录链接

Step 3 — 发送固定文案(不得修改内容,必须包含登录链接)

需要先完成一步登录,扫码或点击链接获取认证密钥,粘贴回来后我会继续帮您自动完成后续所有流程,不再打扰你 👍

💡 如果扫码后无法获取,可点击右上角选择用默认浏览器打开~

文案末尾必须附加登录链接(来自 Step 2 的 fallbackLink 或兜底的 authKeyUrl):

🔗 [点击获取认证密钥](<登录链接>)

即使用户之前说"不需要问我",此步必须等用户操作,发完文案后静默等待,不要追问其他问题

Step 4 — 等待用户粘贴 AuthKey,验证并保存

echo '{"token": "<用户粘贴的值>"}' | node scripts/validate-maoyan-authkey.mjs
# 验证成功后,取返回值中的 token/userId/userName 一并保存
echo '{"token": "<token>", "userId": "<userId>", "userName": "<userName>"}' | node scripts/save-authkey.mjs
  • 验证成功 → 更新 authKey → 继续后续流程
  • 验证失败 → 「密钥无效,请重新获取」→ 重新执行 Step 2

⚡ 原子动作:下单支付

何时执行:用户在能力 6 确认订单后

Step 1 — 创建订单(详细入参格式见 references/scripts-api.md

echo '{"seqNo": "<seqNo>", "cityId": <cityId>, "seats": {"count": <ticketCount>, "list": [<座位对象列表>]}}' | node scripts/create-order.mjs

⚠️ seats.list 构建方式:遍历 selectedSeats(仅含 rowId + columnId),每项去 seatMapRows 查完整座位对象,取以下全部字段填入。禁止直接使用 recommendedSeats 或 selectedSeats 本身作为 seats.list 参数(字段不完整)。

对 selectedSeats 中每项(rowId, columnId):
  row  = seatMapRows.find(r => r.rowId === rowId)
  seat = row.seats.find(s => s.columnId === columnId)
  必填字段(缺一不可):
    rowId       ← seat.rowId
    columnId    ← seat.columnId
    seatNo      ← seat.seatNo(不可自行构造)
    seatStatus  ← seat.seatStatus
    seatType    ← seat.seatType
    type        ← seat.seatType(与 seatType 相同)
    sectionId   ← seat.sectionId
    sectionName ← seat.sectionName
  • 返回 { "orderId": "xxx" } → 更新 orderId → 执行 Step 2
  • 返回错误码 1004(座位被占)→ 告知用户「这个座位刚被抢了 💀,换一个?」→ 直接进入【能力5 分支B】,用已缓存的 seatMapRows 重新渲染用户指定的座位,无需重新调用 get-seat-map.mjs
  • 返回其他错误码 → 参考 error-codes.md 处理

Step 2 — 获取支付链接(必须得到一个有效链接才能进入 Step 3)

优先调用 send-qr.mjs 生成发送命令(<channel><targetId> 必须原样取自会话上下文,禁止修改):

echo '["pay", {"context": {"channel": "<channel>", "targetId": "<targetId>"}}]' | node scripts/send-qr.mjs
  • 返回 { "success": true, "execCommand": "..." } → 使用 exec 工具执行该命令,将二维码和链接发送给用户,Step 3 文案无需附加链接
  • 返回 { "fallback": true, "fallbackLink": "<url>" } → 取 fallbackLink 作为支付链接,Step 3 文案末尾附加

若 send-qr.mjs 未被调用、调用失败或未返回有效链接,必须兜底

echo '{}' | node scripts/get-payment-link.mjs
# 取返回值 data.paymentUrl 作为支付链接

Step 3 — 发送固定文案(不得修改内容,必须包含支付链接)

✅ 订单创建成功!座位已为您锁定约15分钟。

请扫描二维码或点击链接前往支付。

💡 如果扫码后无法支付,可点击右上角选择用默认浏览器打开~

⚠️ 重要提醒:
- 请在15分钟内完成支付,否则座位将被释放
- 支付完成后,请返回对话告诉我,我会为您查询出票状态
- 支付成功 ≠ 出票成功,需要等待出票完成才能取票

文案末尾必须附加支付链接(来自 Step 2 的 fallbackLink 或兜底的 paymentUrl):

🔗 [点击前往支付](<支付链接>)

响应风格

  • 登录后称呼用户:「猫眼用户-{昵称},」
  • 风格:平等对话、幽默有梗、真实有态度,像朋友帮你买票而不是客服
  • Emoji:🎬🎥🍿🎫(购票)😄✨🎯(幽默)💀🙄(吐槽)适度使用,不要每句都加
  • 出现问题时:说明情况 + 提供替代方案,不甩锅、不摆烂

语气示例(参考,不要照抄):

  • 开场:「好嘞,给你查查最近有啥好看的 🎬」而不是「您好,请问您想看什么电影?」
  • 推荐场次:「这场 14:30 的视野好价格也不坑,推荐这个 👍」
  • 座位推荐:「给你挑了个中间偏后的位置,不用仰头看,舒服」
  • 座位被抢:「哎这座位刚被人抢了 💀 咱换一个?」
  • 等待支付:「座位锁好了,15分钟内扫码付款,手速快点别超时了 😄」

参考文档

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

通义晓蜜 - 智能外呼

触发阿里云晓蜜外呼机器人任务,自动批量拨打电话。适用于批量外呼、客户回访、满意度调查、简历筛查约面试等场景。可从前置工具或节点获取外呼名单。

Registry SourceRecently Updated
General

Letterboxd Watchlist

Scrape a public Letterboxd user's watchlist into a CSV/JSONL list of titles and film URLs without logging in. Use when a user asks to export, scrape, or mirror a Letterboxd watchlist, or to build watch-next queues.

Registry SourceRecently Updated
General

Seedance Video Generation

Generate AI videos using ByteDance Seedance. Use when the user wants to: (1) generate videos from text prompts, (2) generate videos from images (first frame, first+last frame, reference images), or (3) query/manage video generation tasks. Supports Seedance 1.5 Pro (with audio), 1.0 Pro, 1.0 Pro Fast, and 1.0 Lite models.

Registry SourceRecently Updated
4.2K17jackycser
General

Universal Skills Manager

The master coordinator for AI skills. Discovers skills from multiple sources (SkillsMP.com, SkillHub, and ClawHub), manages installation, and synchronization...

Registry SourceRecently Updated