/** * 红包功能模块 * 支持用户发红包、AI发红包、开红包动画、钱包余额管理 */ import { getSettings } from './config.js'; import { requestSave } from './save-manager.js'; import { showToast } from './toast.js'; import { escapeHtml, sleep } from './utils.js'; import { refreshChatList } from './ui.js'; import { callAI } from './ai.js'; import { ICON_USER } from './icons.js'; // 当前红包相关状态 let currentRedPacketAmount = ''; let currentRedPacketMessage = '恭喜发财,大吉大利'; let pendingOpenRedPacket = null; // 待打开的红包信息 let pendingOpenContact = null; // 待打开红包的联系人 // ===== 钱包操作函数 ===== /** * 获取钱包余额 */ export function getWalletBalance() { const settings = getSettings(); return parseFloat(settings.walletAmount) || 0; } /** * 从钱包扣款(发红包) */ export function deductFromWallet(amount) { const settings = getSettings(); const current = parseFloat(settings.walletAmount) || 0; if (amount <= 0) { return { success: false, message: '金额必须大于0' }; } if (amount > 200) { return { success: false, message: '单个红包最多200元' }; } if (amount > current) { return { success: false, message: '余额不足' }; } settings.walletAmount = (current - amount).toFixed(2); requestSave(); updateWalletDisplay(); return { success: true, balance: settings.walletAmount }; } /** * 存入钱包(收红包) */ export function addToWallet(amount) { const settings = getSettings(); const current = parseFloat(settings.walletAmount) || 0; settings.walletAmount = (current + amount).toFixed(2); requestSave(); updateWalletDisplay(); return { success: true, balance: settings.walletAmount }; } /** * 更新钱包显示 */ export function updateWalletDisplay() { const el = document.getElementById('wechat-wallet-amount'); if (el) { el.textContent = '¥' + getWalletBalance().toFixed(2); } } // ===== 发红包页面 ===== /** * 显示发红包页面 */ export function showRedPacketPage() { currentRedPacketAmount = ''; currentRedPacketMessage = '恭喜发财,大吉大利'; const page = document.getElementById('wechat-red-packet-page'); if (page) { page.classList.remove('hidden'); updateRedPacketAmountDisplay(); const messageInput = document.getElementById('wechat-red-packet-message'); if (messageInput) { messageInput.value = currentRedPacketMessage; } const amountInput = document.getElementById('wechat-red-packet-amount-input'); if (amountInput) { amountInput.value = ''; } } } /** * 隐藏发红包页面 */ export function hideRedPacketPage() { const page = document.getElementById('wechat-red-packet-page'); if (page) { page.classList.add('hidden'); } } /** * 更新金额显示 */ function updateRedPacketAmountDisplay() { const amountDisplay = document.getElementById('wechat-red-packet-amount-display'); const amountInput = document.getElementById('wechat-red-packet-amount-input'); const amount = amountInput ? parseFloat(amountInput.value) || 0 : parseFloat(currentRedPacketAmount) || 0; if (amountDisplay) { amountDisplay.textContent = '¥ ' + (amount > 0 ? amount.toFixed(2) : '0.00'); } } /** * 显示密码输入弹窗 */ export function showPasswordModal() { const amountInput = document.getElementById('wechat-red-packet-amount-input'); const amount = amountInput ? parseFloat(amountInput.value) || 0 : 0; if (amount <= 0) { showToast('请输入金额'); return; } if (amount > 200) { showToast('单个红包最多200元'); return; } if (amount > getWalletBalance()) { showToast('余额不足'); return; } currentRedPacketAmount = amount.toString(); // 获取祝福语 const messageInput = document.getElementById('wechat-red-packet-message'); if (messageInput && messageInput.value.trim()) { currentRedPacketMessage = messageInput.value.trim(); } const modal = document.getElementById('wechat-red-packet-password-modal'); if (modal) { modal.classList.remove('hidden'); const passwordInput = document.getElementById('wechat-red-packet-password-input'); if (passwordInput) { passwordInput.value = ''; passwordInput.focus(); } } } /** * 隐藏密码输入弹窗 */ export function hidePasswordModal() { const modal = document.getElementById('wechat-red-packet-password-modal'); if (modal) { modal.classList.add('hidden'); } } /** * 验证密码并发送红包 */ function verifyPasswordAndSend() { const passwordInput = document.getElementById('wechat-red-packet-password-input'); const password = passwordInput?.value || ''; if (password.length !== 6) { showToast('请输入6位密码'); return; } const settings = getSettings(); const correctPassword = settings.paymentPassword || '666666'; if (password === correctPassword) { hidePasswordModal(); sendRedPacket(); } else { showToast('密码错误'); if (passwordInput) passwordInput.value = ''; } } /** * 发送红包 */ async function sendRedPacket() { const amount = parseFloat(currentRedPacketAmount) || 0; const message = currentRedPacketMessage; // 扣款 const result = deductFromWallet(amount); if (!result.success) { showToast(result.message, 'info'); return; } // 关闭发红包页面 hideRedPacketPage(); // 触发发送红包事件 const event = new CustomEvent('red-packet-send', { detail: { amount: amount, message: message } }); document.dispatchEvent(event); } // ===== 开红包弹窗 ===== /** * 显示开红包弹窗(AI发的红包) */ export function showOpenRedPacket(redPacketInfo, contact) { pendingOpenRedPacket = redPacketInfo; pendingOpenContact = contact; const modal = document.getElementById('wechat-open-red-packet-modal'); if (!modal) return; // 更新显示内容 const senderName = document.getElementById('wechat-open-rp-sender'); const messageEl = document.getElementById('wechat-open-rp-message'); const previewMsg = document.getElementById('wechat-open-rp-preview-msg'); if (senderName) { senderName.textContent = `${contact?.name || 'AI'}发出的红包`; } if (messageEl) { messageEl.textContent = redPacketInfo.message || '恭喜发财,大吉大利'; } if (previewMsg) { previewMsg.textContent = redPacketInfo.message || '恭喜发财,大吉大利'; } modal.classList.remove('hidden'); } /** * 隐藏开红包弹窗 */ export function hideOpenRedPacket() { const modal = document.getElementById('wechat-open-red-packet-modal'); if (modal) { modal.classList.add('hidden'); } pendingOpenRedPacket = null; pendingOpenContact = null; } /** * 点击"開"按钮,播放开红包动画 */ export async function openRedPacketAnimation() { if (!pendingOpenRedPacket) return; const modal = document.getElementById('wechat-open-red-packet-modal'); const topHalf = document.getElementById('wechat-open-rp-top'); const bottomHalf = document.getElementById('wechat-open-rp-bottom'); if (topHalf && bottomHalf) { // 添加动画类 topHalf.classList.add('slide-up'); bottomHalf.classList.add('slide-down'); // 等待动画完成 await sleep(500); } // 隐藏开红包弹窗 if (modal) { modal.classList.add('hidden'); // 重置动画类 if (topHalf) topHalf.classList.remove('slide-up'); if (bottomHalf) bottomHalf.classList.remove('slide-down'); } // 领取红包 await claimAIRedPacket(); } /** * 领取AI红包 */ async function claimAIRedPacket() { if (!pendingOpenRedPacket || !pendingOpenContact) return; const redPacketInfo = pendingOpenRedPacket; const contact = pendingOpenContact; const settings = getSettings(); // 存入钱包 addToWallet(redPacketInfo.amount); // 更新红包状态 redPacketInfo.status = 'claimed'; redPacketInfo.claimedBy = settings.userName || 'User'; redPacketInfo.claimedAt = Date.now(); // 保存 requestSave(); // 显示红包详情页 showRedPacketDetail(redPacketInfo, contact); // 更新聊天中的红包气泡状态 updateRedPacketBubbleStatus(redPacketInfo.id, 'claimed'); // 在聊天中显示领取提示 const event = new CustomEvent('red-packet-claimed-notice', { detail: { claimerName: settings.userName || 'User', senderName: contact.name } }); document.dispatchEvent(event); pendingOpenRedPacket = null; pendingOpenContact = null; } /** * 显示红包详情页 */ export function showRedPacketDetail(redPacketInfo, contact) { const page = document.getElementById('wechat-red-packet-detail-page'); if (!page) return; const settings = getSettings(); // 更新详情页内容 const senderName = document.getElementById('wechat-rp-detail-sender'); const messageEl = document.getElementById('wechat-rp-detail-message'); const amountEl = document.getElementById('wechat-rp-detail-amount'); const claimerAvatar = document.getElementById('wechat-rp-detail-claimer-avatar'); const claimerName = document.getElementById('wechat-rp-detail-claimer-name'); const claimerTime = document.getElementById('wechat-rp-detail-claimer-time'); const claimerAmount = document.getElementById('wechat-rp-detail-claimer-amount'); if (senderName) { senderName.textContent = `${contact?.name || 'AI'}发出的红包`; } if (messageEl) { messageEl.textContent = redPacketInfo.message || '恭喜发财,大吉大利'; } if (amountEl) { amountEl.textContent = redPacketInfo.amount.toFixed(2); } if (claimerAvatar) { // 使用用户头像 if (settings.userAvatar) { claimerAvatar.innerHTML = `avatar`; } else { claimerAvatar.innerHTML = `${ICON_USER}`; } } if (claimerName) { claimerName.textContent = settings.userName || 'User'; } if (claimerTime) { const now = new Date(); claimerTime.textContent = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`; } if (claimerAmount) { claimerAmount.textContent = redPacketInfo.amount.toFixed(2) + '元'; } page.classList.remove('hidden'); } /** * 隐藏红包详情页 */ export function hideRedPacketDetail() { const page = document.getElementById('wechat-red-packet-detail-page'); if (page) { page.classList.add('hidden'); } } /** * 更新红包气泡状态 */ function updateRedPacketBubbleStatus(redPacketId, status) { const bubble = document.querySelector(`.wechat-red-packet-bubble[data-rp-id="${redPacketId}"]`); if (bubble && status === 'claimed') { bubble.classList.add('claimed'); const statusEl = bubble.querySelector('.wechat-rp-bubble-status'); if (statusEl) { statusEl.textContent = '已领取'; statusEl.classList.remove('hidden'); } } } // ===== 生成红包ID ===== export function generateRedPacketId() { return 'rp_' + Math.random().toString(36).substring(2, 10) + '_' + Date.now(); } // ===== 初始化事件监听 ===== export function initRedPacketEvents() { // 发红包页面返回按钮 document.getElementById('wechat-red-packet-back')?.addEventListener('click', hideRedPacketPage); // 金额输入框变化时更新显示 document.getElementById('wechat-red-packet-amount-input')?.addEventListener('input', updateRedPacketAmountDisplay); // 塞钱进红包按钮 document.getElementById('wechat-red-packet-submit')?.addEventListener('click', showPasswordModal); // 密码弹窗关闭 document.getElementById('wechat-password-modal-close')?.addEventListener('click', hidePasswordModal); // 密码确认按钮 document.getElementById('wechat-red-packet-password-confirm')?.addEventListener('click', verifyPasswordAndSend); // 开红包弹窗关闭 document.getElementById('wechat-open-rp-close')?.addEventListener('click', hideOpenRedPacket); document.getElementById('wechat-open-rp-preview-close')?.addEventListener('click', hideOpenRedPacket); // 开红包按钮 document.getElementById('wechat-open-rp-btn')?.addEventListener('click', openRedPacketAnimation); // 红包详情页返回 document.getElementById('wechat-rp-detail-back')?.addEventListener('click', hideRedPacketDetail); // 监听红包发送事件(用户发红包后,AI 领取) document.addEventListener('red-packet-send', handleUserSendRedPacket); // 监听红包领取提示事件 document.addEventListener('red-packet-claimed-notice', handleRedPacketClaimNotice); } /** * 处理用户发送红包 */ async function handleUserSendRedPacket(event) { const { amount, message } = event.detail; const settings = getSettings(); // 动态导入 chat.js 中的函数,避免循环依赖 const chatModule = await import('./chat.js'); const { currentChatIndex, appendRedPacketMessage, appendRedPacketClaimNotice, showTypingIndicator, hideTypingIndicator, appendMessage, openChat } = chatModule; if (currentChatIndex < 0) return; const contact = settings.contacts[currentChatIndex]; if (!contact) return; const now = new Date(); const timeStr = now.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).replace(/\//g, '-'); // 创建红包信息 const rpInfo = { id: generateRedPacketId(), amount: amount, message: message, senderName: settings.userName || 'User', status: 'pending', claimedBy: null, claimedAt: null, expireAt: Date.now() + 24 * 60 * 60 * 1000 }; // 保存红包消息到聊天记录 if (!contact.chatHistory) contact.chatHistory = []; contact.chatHistory.push({ role: 'user', content: `[红包] ${message}`, time: timeStr, timestamp: Date.now(), isRedPacket: true, redPacketInfo: rpInfo }); // 显示红包消息 appendRedPacketMessage('user', rpInfo, contact); requestSave(); refreshChatList(); // AI 领取红包(延迟 2-5 秒) const claimDelay = 2000 + Math.random() * 3000; await sleep(claimDelay); // 更新红包状态 rpInfo.status = 'claimed'; rpInfo.claimedBy = contact.name; rpInfo.claimedAt = Date.now(); // 更新聊天中的红包气泡状态 updateRedPacketBubbleStatus(rpInfo.id, 'claimed'); // 显示领取提示 appendRedPacketClaimNotice(contact.name, settings.userName || 'User', false); requestSave(); // AI 发送感谢消息(带上下文) await sleep(1000); // 显示打字指示器 showTypingIndicator(contact); try { // 构建提示,让 AI 根据上下文自然回复 const thankPrompt = `用户给你发了一个${amount}元的红包,祝福语是"${message}",请自然地表示感谢,不要使用任何特殊格式标签。`; const aiResponse = await callAI(contact, thankPrompt); hideTypingIndicator(); if (aiResponse && aiResponse.trim()) { // 取第一条回复 let thankMsg = aiResponse.split('|||')[0].trim(); // 移除可能的格式标签 thankMsg = thankMsg.replace(/^\[.*?\]\s*/, ''); if (thankMsg) { contact.chatHistory.push({ role: 'assistant', content: thankMsg, time: new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).replace(/\//g, '-'), timestamp: Date.now() }); appendMessage('assistant', thankMsg, contact); requestSave(); refreshChatList(); } } } catch (e) { console.error('[可乐] AI感谢红包失败:', e); hideTypingIndicator(); } } /** * 处理红包领取提示(用户领取AI红包) */ function handleRedPacketClaimNotice(event) { const { claimerName, senderName } = event.detail; // 动态导入,避免循环依赖 import('./chat.js').then(chatModule => { const { appendRedPacketClaimNotice } = chatModule; appendRedPacketClaimNotice(claimerName, senderName, true); }); }