Files
InnerOS/src/lib/engines/reward-generator.ts
T
jackyu66gitandClaude Opus 4.7 6530b20172 feat: guided light meditation + Edge TTS neural voice audio
- Add continuous guided light-body-scan meditation (16 segments, 97% coverage)
- Edge TTS API endpoint with Xiaoxiao neural voice at -40% rate
- Pre-generated chime WAVs and guidance MP3s in public/audio/
- Mute toggle in practice page, sequenced non-overlapping audio
- Fix middleware to serve /audio and /api/audio/tts without auth
- All LLM output localized to Chinese; fix blank home/settings pages
- DeepSeek API compat: json_object, increased max_tokens
- Analytics, cron reminders, onboarding flow fixes

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 20:08:29 +08:00

154 lines
6.0 KiB
TypeScript

import type { RewardEventType } from "@/generated/prisma/client";
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export interface RewardEvent {
type: RewardEventType;
message: string;
}
interface RewardContext {
isFirstSession: boolean;
streakMilestone: boolean;
streakDays: number;
stageAdvanced: boolean;
newStage: number;
totalSessionsCompleted: number;
}
// ---------------------------------------------------------------------------
// Reward messages
// ---------------------------------------------------------------------------
const FIRST_SESSION_MESSAGES = [
"第一次练习完成了。这条路始于足下,不在于终点。",
"第一天。你不是只想了想,你做到了。这就是一切的区别。",
"欢迎开始你的练习。门已经打开了。明天,再走进来。",
];
const STREAK_3_MESSAGES = [
"连续三天。一个模式正在形成。你的心智开始期待这一刻——习惯正在扎根。",
"第三天。前两次靠的是意志力。今天,是势头的开始。",
];
const STREAK_7_MESSAGES = [
"七天。一整周都在为自己出现。大多数人从未走到这么远。你做到了。",
"一周的每日练习。你的大脑正在实实在在地重塑。神经可塑性不是比喻——它正在发生。",
];
const STREAK_14_MESSAGES = [
"两周。研究表明形成习惯需要21天——你已经走了三分之二,感觉已经不一样了,不是吗?",
"连续十四天。你不再在「尝试冥想」。你是一个在练习的人。身份已经转变。",
];
const STREAK_30_MESSAGES = [
"三十天。一个月的每日练习。无论你开始前是什么样子——焦虑、散乱、反应——你正在变成另一个人。一个能观察自己的心并做出选择的人。",
"一个月。这不再是一个连续记录。这是一项练习。它是你的一部分。问题不再是「我今天要不要练习?」,而是「什么时候?」",
];
const STAGE_UP_MESSAGES: Record<number, string[]> = {
1: [
"你已经进入基础阶段。基本功已经到位。现在开始深入——更长的练习,更细微的觉察。",
"阶段提升:基础。你从尝试进入了训练。练习正在成为熟悉的领域。",
],
2: [
"成长阶段已到。你的坚持是稳固的,你的觉知正在敏锐。这就是练习开始改变你如何在这个世界上行走的地方。",
"阶段提升:成长。你度过了笨拙的初学者阶段。你的练习现在有了质感和深度。",
],
3: [
"稳固阶段。十五次练习和强劲的坚持度。你的内心世界正在变得清晰可读。这就是情绪素养在行动。",
"阶段提升:稳固。你的练习扎下了根。从每天的练习开始,逐渐成为一种存在方式。",
],
4: [
"进阶。这是稀有的领域。大多数人从未发展出这个层次的内心觉知。你的练习现在渗透到你做的每一件事中。",
"阶段提升:进阶。你不只是在练习觉知——你在活出它。「练习」和「生活」之间的界限正在消融。",
],
};
const GENERIC_COMPLETION_MESSAGES = [
"完成。你出现了,你练习了,你反思了。这就是全部——而你在做着。",
"练习完成。留意你现在的感受和之前的区别。那个转变就是意义所在。",
"又一次练习记录在册。不是每一次练习都会感觉深刻——但每一次练习都很重要。",
"你今天完成了功课。不是明天,不是「等事情平静下来的时候」——是今天。这就是自律,自律就是自爱。",
"练习结束。你未来的自己——那个更清晰、更平静、更有选择权的自己——在感谢你。",
];
// ---------------------------------------------------------------------------
// Reward generation
// ---------------------------------------------------------------------------
function pick<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
export function generateRewards(ctx: RewardContext): RewardEvent[] {
const rewards: RewardEvent[] = [];
// First session — highest priority reward
if (ctx.isFirstSession) {
rewards.push({
type: "first_session",
message: pick(FIRST_SESSION_MESSAGES),
});
// Also give a daily completion reward
rewards.push({
type: "daily_completion",
message: pick(GENERIC_COMPLETION_MESSAGES),
});
return rewards;
}
// Always give daily completion
rewards.push({
type: "daily_completion",
message: pick(GENERIC_COMPLETION_MESSAGES),
});
// Streak milestones (only one per session, highest applicable)
if (ctx.streakMilestone) {
if (ctx.streakDays === 30) {
rewards.push({ type: "streak_30", message: pick(STREAK_30_MESSAGES) });
} else if (ctx.streakDays === 14) {
rewards.push({ type: "streak_14", message: pick(STREAK_14_MESSAGES) });
} else if (ctx.streakDays === 7) {
rewards.push({ type: "streak_7", message: pick(STREAK_7_MESSAGES) });
} else if (ctx.streakDays === 3) {
rewards.push({ type: "streak_3", message: pick(STREAK_3_MESSAGES) });
}
}
// Stage advancement
if (ctx.stageAdvanced && ctx.newStage > 0) {
const messages = STAGE_UP_MESSAGES[ctx.newStage];
if (messages) {
rewards.push({ type: "stage_up", message: pick(messages) });
}
}
return rewards;
}
// ---------------------------------------------------------------------------
// Persist rewards to database
// ---------------------------------------------------------------------------
export async function persistRewards(
userId: string,
rewards: RewardEvent[],
prisma: { rewardEvent: { create: (args: { data: any }) => Promise<any> } }
): Promise<void> {
await Promise.all(
rewards.map((r) =>
prisma.rewardEvent.create({
data: {
userId,
type: r.type,
message: r.message,
},
})
)
);
}