增加身份选择
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run

This commit is contained in:
Song367 2025-10-20 18:08:34 +08:00
parent ea465ad586
commit df98105213
5 changed files with 238 additions and 16 deletions

View File

@ -137,7 +137,8 @@ const scenes = [
tag: 'chat', tag: 'chat',
apiKey: 'bot-20250916100919-w8vxr', // 起床场景的API key apiKey: 'bot-20250916100919-w8vxr', // 起床场景的API key
openingLines: [ openingLines: [
" 爷爷/奶奶,我来啦!今天您过得怎么样呀?有没有什么好玩的事儿跟我说说呀?", "我来啦!今天您过得怎么样呀?有没有什么好玩的事儿跟我说说呀?",
"天冷了,您可得多穿点啊!"
] ]
} }
]; ];
@ -487,18 +488,26 @@ io.on('connection', (socket) => {
}); });
// 断开连接 // 断开连接
socket.on('disconnect', () => { socket.on('disconnect', async () => {
console.log('用户断开连接:', socket.id); console.log('用户断开连接:', socket.id);
const client = connectedClients.get(socket.id); const client = connectedClients.get(socket.id);
if (client) { if (client) {
// 广播用户离开事件 // 广播用户离开事件
socket.broadcast.emit('user-disconnected', { socket.broadcast.emit('user-disconnected', {
id: socket.id, id: socket.id,
username: client.username username: client.username
}); });
} }
connectedClients.delete(socket.id); connectedClients.delete(socket.id);
// 清空聊天记录
try {
await messageHistory.clearHistory();
console.log('断开连接后已清空 chat_history.json');
} catch (err) {
console.error('清空聊天记录失败:', err);
}
// 清除活跃用户 // 清除活跃用户
if (activeUser === socket.id) { if (activeUser === socket.id) {
activeUser = null; activeUser = null;

View File

@ -197,7 +197,7 @@ async function chatWithAudioStream(userInput) {
} }
// 导出初始化函数,供外部调用 // 导出初始化函数,供外部调用
export { chatWithAudioStream, initializeHistoryMessage, getCurrentHistoryMessage, saveMessage, updateHistoryMessage }; export { chatWithAudioStream, initializeHistoryMessage, getCurrentHistoryMessage, saveMessage, updateHistoryMessage, prependIntroRole };
// 处理音频播放队列 // 处理音频播放队列
async function processAudioQueue() { async function processAudioQueue() {
@ -323,3 +323,18 @@ async function playAudioStreamNode(audioHex) {
// export { chatWithAudioStream, playAudioStream, playAudioStreamNode, initializeHistoryMessage, getCurrentHistoryMessage }; // export { chatWithAudioStream, playAudioStream, playAudioStreamNode, initializeHistoryMessage, getCurrentHistoryMessage };
// 在历史消息顶部插入“我是你的roleName / 好的roleName。”开场提示
function prependIntroRole(roleName) {
if (!roleName) return;
const introUser = { role: 'user', content: `我是你的${roleName}` };
const introAssistant = { role: 'assistant', content: `好的,${roleName}` };
const hasIntro = historyMessage.slice(0, 2).some(m =>
m.content === introUser.content || m.content === introAssistant.content
);
if (!hasIntro) {
// 先插助手,再插用户,确保用户消息在最顶部
historyMessage.unshift(introAssistant);
historyMessage.unshift(introUser);
}
}

View File

@ -56,7 +56,7 @@
</div> </div>
<div class="flex flex-col md:flex-row items-center justify-center gap-8 md:gap-16 w-full" data-aos="fade-up"> <div class="flex flex-col md:flex-row items-center justify-center gap-8 md:gap-16 w-full" data-aos="fade-up">
<a href="/index.html" class="group block cursor-pointer focus:outline-none"> <a href="/old.html" class="group block cursor-pointer focus:outline-none">
<div class="avatar-hover bg-gray-800 bg-opacity-60 backdrop-blur-md rounded-full p-4 border-2 border-green-500 transition-all duration-300 group-hover:border-purple-500 md:group-hover:border-purple-500 group-active:border-purple-500"> <div class="avatar-hover bg-gray-800 bg-opacity-60 backdrop-blur-md rounded-full p-4 border-2 border-green-500 transition-all duration-300 group-hover:border-purple-500 md:group-hover:border-purple-500 group-active:border-purple-500">
<div class="w-32 h-32 md:w-40 md:h-40 rounded-full overflow-hidden border-4 border-green-400 group-hover:border-purple-400 md:group-hover:border-purple-400 group-active:border-purple-400 transition-all duration-300"> <div class="w-32 h-32 md:w-40 md:h-40 rounded-full overflow-hidden border-4 border-green-400 group-hover:border-purple-400 md:group-hover:border-purple-400 group-active:border-purple-400 transition-all duration-300">
<img src="tx.png" alt="Dashboard" class="w-full h-full object-cover"> <img src="tx.png" alt="Dashboard" class="w-full h-full object-cover">

View File

@ -1,7 +1,7 @@
console.log('视频文件:'); console.log('视频文件:');
// WebRTC 音视频通话应用 // WebRTC 音视频通话应用
// import { chatWithAudioStream } from './chat_with_audio.js'; // import { chatWithAudioStream } from './chat_with_audio.js';
import { chatWithAudioStream, initializeHistoryMessage, updateHistoryMessage } from './chat_with_audio.js'; import { chatWithAudioStream, initializeHistoryMessage, updateHistoryMessage, prependIntroRole } from './chat_with_audio.js';
import { AudioProcessor } from './audio_processor.js'; import { AudioProcessor } from './audio_processor.js';
@ -256,7 +256,11 @@ class WebRTCChat {
async initializeHistory() { async initializeHistory() {
try { try {
await initializeHistoryMessage(100); await initializeHistoryMessage(100);
const params = new URLSearchParams(window.location.search);
const roleName = params.get('roleName');
if (roleName) {
prependIntroRole(roleName);
}
console.log('历史消息初始化完成'); console.log('历史消息初始化完成');
} catch (error) { } catch (error) {
console.error('历史消息初始化失败:', error); console.error('历史消息初始化失败:', error);
@ -268,16 +272,22 @@ class WebRTCChat {
try { try {
console.log('开始初始化开场白音频...'); console.log('开始初始化开场白音频...');
// 获取URL参数中的roleName
const params = new URLSearchParams(window.location.search);
const roleName = params.get('roleName') || '';
// 获取当前场景的开场白 // 获取当前场景的开场白
const response = await fetch('/api/current-scene/opening-line'); const response = await fetch('/api/current-scene/opening-line');
const data = await response.json(); const data = await response.json();
if (data.success && data.openingLine) { if (data.success && data.openingLine) {
console.log(`获取到开场白: ${data.openingLine}`); // 如果有roleName则在开场白前加上角色名称
const finalOpeningLine = roleName ? `${roleName}${data.openingLine}` : data.openingLine;
console.log(`获取到开场白: ${finalOpeningLine}`);
// 生成开场白音频 // 生成开场白音频
await this.generateOpeningAudio(data.openingLine); await this.generateOpeningAudio(finalOpeningLine);
this.logMessage(`开场白音频已准备就绪: ${data.openingLine}`, 'success'); this.logMessage(`开场白音频已准备就绪: ${finalOpeningLine}`, 'success');
} else { } else {
console.warn('未获取到开场白:', data.message); console.warn('未获取到开场白:', data.message);
} }

188
src/old.html Normal file
View File

@ -0,0 +1,188 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Yantootech</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.net.min.js"></script>
<style>
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 15px 30px rgba(252, 118, 7, 0.2);
}
.selected-card {
animation: pulse 2s infinite;
box-shadow: 0 0 0 4px rgba(252, 118, 7, 0.3);
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(252, 118, 7, 0.4); }
70% { box-shadow: 0 0 0 15px rgba(252, 118, 7, 0); }
100% { box-shadow: 0 0 0 0 rgba(252, 118, 7, 0); }
}
.button-glow {
transition: all 0.3s ease;
}
.button-glow:hover {
box-shadow: 0 0 20px rgba(252, 118, 7, 0.6);
}
</style>
</head>
<body class="bg-[#EEECBC] min-h-screen font-sans">
<div id="vanta-bg" class="fixed inset-0 z-0"></div>
<div class="relative z-10 container mx-auto px-4 py-12">
<div class="text-center mb-16">
<h1 class="text-4xl md:text-5xl font-bold text-[#D83514] mb-4">选择你的身份</h1>
<p class="text-lg text-[#AC8975] max-w-2xl mx-auto">在数字世界找到属于你的温暖连接</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 max-w-4xl mx-auto">
<!-- Uncle Card -->
<div class="card bg-[#EFCE7D] rounded-xl p-6 text-center cursor-pointer transition-all duration-300 border-2 border-[#AC8975] card-hover" onclick="selectRole('uncle')">
<div class="bg-white rounded-full w-24 h-24 mx-auto mb-4 flex items-center justify-center text-4xl">
👨‍💼
</div>
<h3 class="text-2xl font-semibold text-[#D83514] mb-2">叔叔</h3>
<!-- <p class="text-[#AC8975]">智慧与经验的分享者</p> -->
<div class="selected-indicator hidden mt-4 text-[#FC7607]">
<i data-feather="check-circle" class="w-8 h-8 mx-auto"></i>
</div>
</div>
<!-- Aunt Card -->
<div class="card bg-[#EFCE7D] rounded-xl p-6 text-center cursor-pointer transition-all duration-300 border-2 border-[#AC8975] card-hover" onclick="selectRole('aunt')">
<div class="bg-white rounded-full w-24 h-24 mx-auto mb-4 flex items-center justify-center text-4xl">
👩‍💼
</div>
<h3 class="text-2xl font-semibold text-[#D83514] mb-2">阿姨</h3>
<!-- <p class="text-[#AC8975]">温柔与关怀的传递者</p> -->
<div class="selected-indicator hidden mt-4 text-[#FC7607]">
<i data-feather="check-circle" class="w-8 h-8 mx-auto"></i>
</div>
</div>
<!-- Grandpa Card -->
<div class="card bg-[#EFCE7D] rounded-xl p-6 text-center cursor-pointer transition-all duration-300 border-2 border-[#AC8975] card-hover" onclick="selectRole('grandpa')">
<div class="bg-white rounded-full w-24 h-24 mx-auto mb-4 flex items-center justify-center text-4xl">
👴🌳
</div>
<h3 class="text-2xl font-semibold text-[#D83514] mb-2">爷爷</h3>
<!-- <p class="text-[#AC8975]">故事与智慧的宝库</p> -->
<div class="selected-indicator hidden mt-4 text-[#FC7607]">
<i data-feather="check-circle" class="w-8 h-8 mx-auto"></i>
</div>
</div>
<!-- Grandma Card -->
<div class="card bg-[#EFCE7D] rounded-xl p-6 text-center cursor-pointer transition-all duration-300 border-2 border-[#AC8975] card-hover" onclick="selectRole('grandma')">
<div class="bg-white rounded-full w-24 h-24 mx-auto mb-4 flex items-center justify-center text-4xl">
👵🧶
</div>
<h3 class="text-2xl font-semibold text-[#D83514] mb-2">奶奶</h3>
<!-- <p class="text-[#AC8975]">爱与传统的守护者</p> -->
<div class="selected-indicator hidden mt-4 text-[#FC7607]">
<i data-feather="check-circle" class="w-8 h-8 mx-auto"></i>
</div>
</div>
</div>
<div class="text-center mt-16">
<button id="confirmBtn" class="bg-[#AC8975] bg-opacity-60 text-gray-500 px-8 py-4 rounded-full text-xl font-medium transition-all duration-300 cursor-not-allowed">
确认选择
</button>
</div>
</div>
<script>
if (window.THREE && window.VANTA && document.querySelector('#vanta-bg')) {
VANTA.NET({
el: "#vanta-bg",
THREE: window.THREE, // 显式传入 THREE避免读取不到全局
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0xfc7607,
backgroundColor: 0xeeecbc,
points: 8.00,
maxDistance: 20.00,
spacing: 15.00
});
} else {
console.error('VANTA 初始化失败THREE 或 VANTA 未加载');
}
// 初始化 feather icons
feather.replace();
let selectedRole = null;
const roleNames = {
'uncle': '叔叔',
'aunt': '阿姨',
'grandpa': '爷爷',
'grandma': '奶奶'
};
function selectRole(role) {
// Remove selection from all cards
document.querySelectorAll('.card').forEach(card => {
card.classList.remove('selected-card', 'border-[#FC7607]');
card.querySelector('.selected-indicator').classList.add('hidden');
card.style.transform = '';
});
// Add selection to clicked card
const selectedCard = event.currentTarget;
selectedCard.classList.add('selected-card', 'border-[#FC7607]');
selectedCard.querySelector('.selected-indicator').classList.remove('hidden');
selectedCard.style.transform = 'scale(1.05)';
selectedRole = role;
// Enable confirm button
const confirmBtn = document.getElementById('confirmBtn');
confirmBtn.classList.remove('bg-[#AC8975]', 'bg-opacity-60', 'text-gray-500', 'cursor-not-allowed');
confirmBtn.classList.add('bg-gradient-to-r', 'from-[#FC7607]', 'to-[#D83514]', 'text-white', 'button-glow', 'cursor-pointer');
confirmBtn.textContent = `小乐与 ${roleNames[role]} 开始对话`;
// Add animation to the button
confirmBtn.style.animation = 'none';
setTimeout(() => {
confirmBtn.style.animation = 'pulse 1.5s infinite';
}, 10);
}
// Add hover effect to cards
document.querySelectorAll('.card').forEach(card => {
card.addEventListener('mouseenter', () => {
if (!card.classList.contains('selected-card')) {
card.classList.add('border-[#FC7607]');
}
});
card.addEventListener('mouseleave', () => {
if (!card.classList.contains('selected-card')) {
card.classList.remove('border-[#FC7607]');
}
});
});
// Confirm button action
document.getElementById('confirmBtn').addEventListener('click', function() {
if (selectedRole) {
const roleName = roleNames[selectedRole];
window.location.href = `index.html?roleName=${encodeURIComponent(roleName)}`;
}
});
</script>
</body>
</html>