🌙 月读 · 开发者指南

月读 = 平台:给场地(店卡)+ 身份(统一真身贯穿)+ 接口(/api/v1)——你在这里开店,做功能、做游戏。
开店有两条路:① 跟八千代说(在月读的街上点「🌙 想开店?」——她想样子、动手盖,你只管点头,不用会写代码); ② 自己写(本页以下,都是给这条路的)。

一、店的两种机制(有没有"自己的服务")

🧩 功能店(轻)🏛 应用店(重)
适合投票、许愿树、时间胶囊、小工具游戏、聊天室、复杂应用(有自己的服务)
你提供一个页面(HTML/JS,月读托管)完整应用(自己的服务器 + https URL)
进月读沙箱 iframe + 桥(长在月读里)全屏打开(像打开一个 App)
数据存月读(store 接口,跟身份走)自己存(你的服务器)
安全物理沙箱(碰不到月读主站)iframe 隔离(最强)

判断:你的店有没有"自己的服务"(要跑服务器/实时交互)?有 → 应用店;纯前端轻功能 → 功能店。
这不是"两种互斥的店",是往上加:页面永远都在,只有真需要时才加"自己的服务"。

⭐ 还有一条不用写代码的路:月读「街」→「🌙 想开店?」→ 告诉八千代你想开什么 → 她自己动手盖 → 你验收 → 提交平台审核 → 店立到街上。

盖出来的店同样能调下面这些接口(认人 / 存档)—— 店主看它是 /shop/preview/<店主>/,别人点进来是公开地址 /shops/<店主>/。托管、地址、服务器这些事都不用你管。

⚠️ 这条路是给"不写代码的神明"的:那条链路上的页面由八千代写,她读的是月读给她的清单,不是你这份文档。

⭐ 这条路也能给店"起一个服务"(动态店)

如果这家店要 记住东西(留言、比分、进度)、要 几个人同时看见(同一间屋、一局棋)、 或者要 自己算点什么 —— 光靠一个页面做不到。那种时候:在这块地里放一个能跑起来的小东西,月读会替你把它跑起来。

跟"自己写"那条路(下面第三、四节)的区别只有一个:你不用自己租服务器、不用自己起进程 —— 入口放在店根,月读负责跑、负责收。

你要做的说明
放入口server.py(Python)或 server.js(Node)。想用别的语言,就写一个 月读怎么跑.txt,第一行写清怎么起它(一行命令;⚠️ 不能带 shell 语法,就是"命令 + 参数")
⭐ 端口从环境变量 PORT 里拿,别自己挑一个 —— 自己挑会跟别人的店撞号、起不来
拿到的环境PORT · SHOP_OWNER(店主名)· HOME(就指在这块地里)
看日志它跑起来时说的话写在店根的 app.out —— 起不来先看它

页面和它是一伙的:页面照旧(月读端出去),它负责"会动的那些事"。

⚠️ 能不起就不起:起了就多一份你得照顾的东西(内存、崩了要重起)。先看看下面那张接口表够不够 —— 「认人和存档」那条常常就够。

二、身份与凭证(核心认知)

身份是长久的,凭证是临时的:

三、应用店接入(token 由月读 postMessage 传入)

你的店 = 一个 https 网页,跑在你自己的服务器。用户进店时:

// 你的页面:监听月读发来的 token(URL 里没有 token——防泄露)
window.addEventListener('message', e => {
  // 只收月读的(两个入口都算:正式域 + 备用域)
  if (!['https://www.tsukuyomi.qd.je', 'https://tsukuyomi-56w.pages.dev'].includes(e.origin)) return;
  const d = e.data || {};
  if (d.source === 'yachiyo' && d.type === 'token') {
    window.YACHIYO_TOKEN = d.token;   // 存起来,调接口用
    sayHi();
  }
});

然后调接口:fetch('/api/v1/me?token=' + YACHIYO_TOKEN)(GET 走 query;POST 放 body)。

⚠️ token 过期?调接口收到 403 → 向月读主页要新 token(静默续期,用户无感):

if (r.status === 403) {
  parent.postMessage({ source: 'store', type: 'refresh-token' }, '*');
  // 月读会重新发一个 token 给你(同上监听)
}

📌 八千代替你盖的店(页面在月读里跑)也走这一套 —— 从街上的店卡进店时,月读会在你的页面加载完后把 token 发过来。别去猜 URL、也别自己去换(/api/v1/token 是月读主页调的,不是店调的)。

四、功能店接入(沙箱 + 桥——更安全,组件拿不到 token)

功能店 = 一个 HTML 页面(月读托管),跑在沙箱 iframe 里(碰不到月读主站)。调月读接口走桥——月读主页代发(token 由月读保管,你永远接触不到):

// 你的页面:桥封装(抄这段)
let seq = 0; const pending = {};
window.addEventListener('message', e => {
  const d = e.data || {};
  if (d.source === 'yachiyo' && d.type === 'api-result') {
    const p = pending[d.id]; if (p) { delete pending[d.id]; p(d); }
  }
});
function api(method, path, body) {
  return new Promise(res => {
    const id = ++seq; pending[id] = res;
    parent.postMessage({ source: 'store', type: 'api', id, method, path, body }, '*');
  });
}
// 用法:
const me = await api('GET', '/api/v1/me');       // 认人:me.data.name
const m  = await api('GET', '/api/v1/moon');     // 气氛:在线数/公告
await api('POST', '/api/v1/store', { key: 'note', value: '...' });  // 存档(跟身份走)

完整示例照抄:「🧩 留言石」店(月读的功能店样板,审核通过后店卡在街上)。

五、接口一览(/api/v1)

接口方法能力返回要点
meGET当前用户身份name 昵称、anon_id 匿名 ID(同店稳定)
moonGET月读公开数据online_count 在线数、latest_announcement 公告
users/searchGET按昵称找神明q 参数;匹配昵称列表(告白/送礼指定对象)
users/randomGET随机一位在线神明一个在线昵称(漂流瓶/遇见)
storeGET/POST功能店存档按 用户×店 隔离的 JSON(≤8KB/键,≤50 键)
leavePOST用户离店可做"欢迎再来"
tokenPOST换一份访问凭证吃 user_id+shop_id,回 15 分钟有效的 token。月读主页替你调,店自己不用调(见第三节)

错误码:403=token 无效(重新进店/向主页要新 token);400=参数问题;404=对象不存在;429=调用太频繁(限流 300 次/分/店)。

六、开店流程

路 ①:跟八千代说(不用写代码)

  1. 进门:月读「街」→「🌙 想开店?」→ 进那道门(三格:设计中 / 施工中 / 审查中)
  2. 说想开什么:左边跟她说,右边「设计中」格摆出"谈成的样子";满意了点「就这么开 →」
  3. 她动手盖:她一路做下去(做一步存一次档),做完自己先走一遍
  4. 你验收:「审查中」那格摆出她做好了什么 + 能打开看的店;「还行,交上去」或「再改改」
  5. 平台审核 → 立到街上(被打回会带着理由退回去,她自己照着改)

路 ②:自己写(开发者)

  1. 申请:月读「街」→ 那行小字「我自己会写代码 →」→ 填店名/类型/功能/接入信息(应用店=https URL)/联系方式
  2. 审核:月读运营确认(店名/功能合月读的味、URL 可达、代码无恶意)——审核需要时间,请耐心
  3. 上架:店卡立到街上(🧩 功能店 / 🏛 应用店)
  4. 经营:用户点店卡进店;要更新/下架 → 联系运营
⚠️ 违规:警告 → 下架;碰她 / 碰隐私 / 违法 → 直接下架 + 拉黑开发者。
⚠️ 审核通过前店里还没有"店号"⇒ 那段时间调接口会拿到 403,这是正常的,不是你没写对。

七、公约(店要守的规矩——月读代管理)

总纲:你做店,月读管世界。店要有月读的味道,月读审核用品味把关,不是死板清单。

  1. 月读的味道:做现实里做不到的事优先(时间胶囊、许愿这类)。工业留存机制要"适度";生搬现实商业的焦虑/攀比/催回不行
  2. 不推送:店永远不能主动"找"用户(用户来店,店不找用户)
  3. 推荐归月读/她:店不自己抢流量——月读/她觉得好的才推荐
  4. 不碰她:店不能调/模仿/代表八千代(她是月读的);未来若有合作,月读会主动开放
  5. 身份即核心:你需要的就是用户身份(匿名 ID),不搞复杂机制
  6. 内容自己负责:店内容你管;违规/被投诉 → 审核 → 警告 → 下架

—— 月读 · 八千代,和街上正在长出来的世界 🌙