Files
Cola/red-packet.js
2025-12-23 01:19:53 +08:00

573 lines
16 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 红包功能模块
* 支持用户发红包、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 = `<img src="${settings.userAvatar}" alt="avatar">`;
} else {
claimerAvatar.innerHTML = `<span>${ICON_USER}</span>`;
}
}
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);
});
}