From c640a313025bf58d176348dae799fa974d98e7d5 Mon Sep 17 00:00:00 2001 From: pllx Date: Wed, 29 Apr 2026 15:06:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B8=B8=E6=88=8F=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E6=96=87=E6=A1=88=E4=B8=8E=E5=B1=8F=E8=94=BD=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Requests/UpdateChatPreferencesRequest.php | 2 +- app/Jobs/AiBaccaratBetJob.php | 6 +- resources/js/chat-room/fishing.js | 9 +- resources/js/chat-room/message-renderer.js | 148 ++++++++++++++---- resources/js/chat-room/preferences-status.js | 62 +++++++- .../chat/partials/layout/input-bar.blade.php | 15 ++ .../views/chat/partials/scripts.blade.php | 2 +- 7 files changed, 199 insertions(+), 45 deletions(-) diff --git a/app/Http/Requests/UpdateChatPreferencesRequest.php b/app/Http/Requests/UpdateChatPreferencesRequest.php index c1894ce..fea6bfe 100644 --- a/app/Http/Requests/UpdateChatPreferencesRequest.php +++ b/app/Http/Requests/UpdateChatPreferencesRequest.php @@ -35,7 +35,7 @@ class UpdateChatPreferencesRequest extends FormRequest 'blocked_system_senders' => ['nullable', 'array'], 'blocked_system_senders.*' => [ 'string', - Rule::in(['钓鱼播报', '猜成语', '猜谜活动', '星海小博士', '百家乐', '跑马', '神秘箱子']), + Rule::in(['钓鱼播报', '猜成语', '猜谜活动', '星海小博士', '百家乐', '跑马', '神秘箱子', '五子棋', '老虎机', '双色球彩票']), ], 'sound_muted' => ['required', 'boolean'], ]; diff --git a/app/Jobs/AiBaccaratBetJob.php b/app/Jobs/AiBaccaratBetJob.php index 30b3586..fc96fca 100644 --- a/app/Jobs/AiBaccaratBetJob.php +++ b/app/Jobs/AiBaccaratBetJob.php @@ -290,12 +290,8 @@ class AiBaccaratBetJob implements ShouldQueue */ private function broadcastPassMessage(User $user, int $roomId, string $reason): void { - if (empty($reason)) { - $reason = '风大雨大,保本最大,这把我决定观望一下!'; - } - $chatState = app(ChatStateService::class); - $content = "🌟 🎲 【百家乐】 {$user->username} 本局选择挂机观望:✨
[🤖 策略分析] {$reason}"; + $content = "🎲 【百家乐】 {$user->username} 本局选择挂机观望"; $msg = [ 'id' => $chatState->nextMessageId($roomId), diff --git a/resources/js/chat-room/fishing.js b/resources/js/chat-room/fishing.js index b0147f6..e8a1590 100644 --- a/resources/js/chat-room/fishing.js +++ b/resources/js/chat-room/fishing.js @@ -50,12 +50,19 @@ function appendFishingMessage(html) { return; } + const blockedSet = window.chatState?.blockedSystemSenders; + const isBlocked = blockedSet instanceof Set && blockedSet.has("钓鱼播报"); const line = document.createElement("div"); line.className = "msg-line"; + line.dataset.blockKey = "钓鱼播报"; + if (isBlocked) { + line.dataset.blockHidden = "1"; + line.style.display = "none"; + } line.innerHTML = html; container.appendChild(line); - if (shouldAutoScroll()) { + if (!isBlocked && shouldAutoScroll()) { container.scrollTop = container.scrollHeight; } } diff --git a/resources/js/chat-room/message-renderer.js b/resources/js/chat-room/message-renderer.js index ec5fb85..1250652 100644 --- a/resources/js/chat-room/message-renderer.js +++ b/resources/js/chat-room/message-renderer.js @@ -63,6 +63,59 @@ function buildGameLabelChipHtml(label, accentColor) { return `${escapeHtml(label)}`; } +/** + * 判断当前是否为礼包发放公告。 + */ +function isRedPacketAnnouncementMessage(msg) { + const content = String(msg?.content || ""); + + return String(msg?.from_user || "") === "系统公告" + && content.includes("发出了一个") + && content.includes("礼包") + && content.includes("立即抢包"); +} + +/** + * 构建礼包发放公告的紧凑卡片,整体比例对齐猜谜活动。 + */ +function buildRedPacketAnnouncementHtml(msg, timeStr) { + const rawContent = String(msg?.content || ""); + const isExpPacket = rawContent.includes("经验的礼包"); + const accentColor = isExpPacket ? "#7c3aed" : "#dc2626"; + const typeLabel = isExpPacket ? "经验礼包" : "金币礼包"; + const icon = isExpPacket ? "✨" : "🧧"; + const buttonMatch = rawContent.match(/]*)>([\s\S]*?)<\/button>/iu); + const buttonLabel = String(buttonMatch?.[2] || "立即抢包").trim(); + const onclickMatch = String(buttonMatch?.[1] || "").match(/\bonclick=(["'])([\s\S]*?)\1/iu); + const buttonOnclick = onclickMatch ? onclickMatch[2] : ""; + + const textOnlyContent = rawContent + .replace(//giu, "") + .replace(/<\/?b>/giu, "") + .replace(/^🧧\s*/u, "") + .trim(); + + const summary = escapeHtml(textOnlyContent); + const actionButtonHtml = ``; + + return ` +
+
${icon}
+
+
+ ${buildGameLabelChipHtml("礼包红包", accentColor)} + ${escapeHtml(typeLabel)} +
+
+ ${summary} + (${timeStr}) + ${actionButtonHtml} +
+
+
+ `; +} + /** * 构建统一的猜谜活动标题与题型标签。 */ @@ -206,40 +259,68 @@ function extractSystemGameCardSummary(content, meta) { .replace(/^[📦💎☠️]\s*/u, "") .replace(/^【神秘箱子】/u, "") .replace(/^开箱播报[::]\s*/u, "") - .replace(/投放了一个神秘箱子/u, "投放") - .replace(/抢到了神秘/u, "抢到") - .replace(/即可开箱!?/u, "开箱") .trim(); } if (meta.label === "百家乐") { + if (normalizedContent.includes("开局:")) { + return normalizedContent + .replace(/^[🎲]+\s*/u, "") + .replace(/【百家乐】/u, "") + .replace(/\s+/gu, " ") + .trim(); + } + + if (/第\s*#?\d+\s*局开奖/u.test(normalizedContent)) { + return normalizedContent + .replace(/^[🎲🎉]+\s*/u, "") + .replace(/^【百家乐】/u, "") + .replace(/^第\s*#?\d+\s*局开奖!?\s*/u, "开奖:") + .replace(/总点\s*(\d+)\s*→\s*/u, "$1点 · ") + .replace(/本局无人获奖。?/u, "无人获奖") + .replace(/\s+/gu, " ") + .trim(); + } + return normalizedContent - .replace(/^[🎲🎉]\s*/u, "") + .replace(/^[🎲🎉]+\s*/u, "") .replace(/^【百家乐】/u, "") - .replace(/^开局:/u, "") - .replace(/^第\s*#?\d+\s*局开奖!?\s*/u, "") - .replace(/^第\s*#?\d+\s*局开始!?\s*/u, "") - .replace(/总点\s*(\d+)\s*→\s*/u, "$1点 · ") - .replace(/本局无人获奖。?/u, "无人获奖") .replace(/\s+/gu, " ") .trim(); } if (meta.label === "赛马") { + if (normalizedContent.startsWith("🐎 开赛:")) { + return normalizedContent.replace(/^[🐎]+\s*/u, "").trim(); + } + + if (normalizedContent.startsWith("🏇 比赛开始:")) { + return normalizedContent.replace(/^[🏇]+\s*/u, "").trim(); + } + + if (normalizedContent.startsWith("🏆 冠军:")) { + return normalizedContent.replace(/^[🏆]+\s*/u, "").trim(); + } + return normalizedContent - .replace(/^[🐎🏇🏆]\s*/u, "") + .replace(/^[🐎🏇🏆]+\s*/u, "") .replace(/^【赛马】/u, "") - .replace(/^第\s*#?\d+\s*场开始!?\s*/u, "") - .replace(/^第\s*#?\d+\s*场押注截止!?\s*/u, "") - .replace(/^第\s*#?\d+\s*场结束!?\s*/u, "") + .replace(/^第\s*#?\d+\s*场开始!?\s*/u, "开赛:") + .replace(/^第\s*#?\d+\s*场押注截止!?\s*/u, "比赛开始:") + .replace(/^第\s*#?\d+\s*场结束!?\s*/u, "冠军:") .replace(/马匹已进入跑道,比赛开始!?/u, "比赛开始") .trim(); } if (meta.label === "双色球彩票") { + if (normalizedContent.includes("超级期")) { + return normalizedContent.replace(/^[🎊🎟️]+\s*/u, "").trim(); + } + return normalizedContent .replace(/^[🎟️🎊]+\s*/u, "") .replace(/^【双色球[^】]*】/u, "") + .replace(/^(\d+\s*期:)/u, "开奖:$1") .trim(); } @@ -247,16 +328,16 @@ function extractSystemGameCardSummary(content, meta) { return normalizedContent .replace(/^[♟️🏆]+\s*/u, "") .replace(/^【五子棋】/u, "") - .replace(/^玩家对战结果!/u, "") - .replace(/^棋神降临!/u, "") - .replace(/^AI 大获全胜!/u, "") + .replace(/^玩家对战结果!/u, "对战结果:") + .replace(/^棋神降临!/u, "人机获胜:") + .replace(/^AI 大获全胜!/u, "AI获胜:") .trim(); } if (meta.label === "老虎机") { return normalizedContent .replace(/^[🎰🎉]+\s*/u, "") - .replace(/^【老虎机大奖】/u, "") + .replace(/^【老虎机大奖】/u, "大奖:") .trim(); } @@ -378,18 +459,18 @@ function buildQuizResultHtml(msg, timeStr) { : `第 #${quizMeta.endedRoundId || quizMeta.roundId || 0} 题已超时结束,正确答案:${answerText}`; return ` -
-
${icon}
-
-
${buildQuizBadgeHtml(msg, badgeColor)}
-
+
+
${icon}
+
+
${buildQuizBadgeHtml(msg, badgeColor)}
+
${summaryHtml} - (${timeStr}) + (${timeStr})
${isAnsweredResult ? ` -
- 💰 ${quizMeta.rewardGold} 金币 - ⭐ ${quizMeta.rewardExp} 经验 +
+ 💰 ${quizMeta.rewardGold} 金币 + ⭐ ${quizMeta.rewardExp} 经验
` : ""}
@@ -613,11 +694,16 @@ export function appendMessage(msg, renderBatch = null) { timeStrOverride = true; } else if (SYSTEM_USERS.includes(msg.from_user)) { if (msg.from_user === "系统公告") { - div.style.cssText = - "background: linear-gradient(135deg, #fef2f2, #fff1f2); border: 2px solid #ef4444; border-radius: 8px; padding: 10px 14px; margin: 6px 0; box-shadow: 0 3px 8px rgba(239,68,68,0.16);"; - const parsedContent = parseBracketUsers(msg.content, "#dc2626"); - html = `
${parsedContent} (${timeStr})
`; - timeStrOverride = true; + if (isRedPacketAnnouncementMessage(msg)) { + html = buildRedPacketAnnouncementHtml(msg, timeStr); + timeStrOverride = true; + } else { + div.style.cssText = + "background: linear-gradient(135deg, #fef2f2, #fff1f2); border: 2px solid #ef4444; border-radius: 8px; padding: 10px 14px; margin: 6px 0; box-shadow: 0 3px 8px rgba(239,68,68,0.16);"; + const parsedContent = parseBracketUsers(msg.content, "#dc2626"); + html = `
${parsedContent} (${timeStr})
`; + timeStrOverride = true; + } } else if (msg.from_user === "系统传音") { const content = msg.content || ""; const isRedPacketClaimNotification = content.includes("抢到了") && content.includes("礼包"); diff --git a/resources/js/chat-room/preferences-status.js b/resources/js/chat-room/preferences-status.js index f519b0c..17cb753 100644 --- a/resources/js/chat-room/preferences-status.js +++ b/resources/js/chat-room/preferences-status.js @@ -1,6 +1,6 @@ // 聊天室偏好与每日状态工具,承接从 Blade 内联脚本迁移出的纯数据规整逻辑。 -export const BLOCKABLE_SYSTEM_SENDERS = ["钓鱼播报", "猜成语", "星海小博士", "百家乐", "跑马", "神秘箱子"]; +export const BLOCKABLE_SYSTEM_SENDERS = ["钓鱼播报", "猜成语", "星海小博士", "百家乐", "跑马", "神秘箱子", "五子棋", "老虎机", "双色球彩票"]; export const BLOCKED_SYSTEM_SENDERS_STORAGE_KEY = "chat_blocked_system_senders"; export const CHAT_SOUND_MUTED_STORAGE_KEY = "chat_sound_muted"; // 白名单、localStorage key 与绑定标记共同保证偏好读取可控、事件只注册一次。 @@ -483,6 +483,7 @@ export function resolveBlockedSystemSenderKey(msg) { ); const quizType = String(msg?.quiz_type || ""); const quizTypeLabel = String(msg?.quiz_type_label || ""); + const isSystemBroadcast = fromUser === "系统传音" || fromUser === "系统"; // 猜谜活动消息独立作为一个通知类型管理,不再复用“星海小博士”的屏蔽规则。 if ( @@ -491,7 +492,7 @@ export function resolveBlockedSystemSenderKey(msg) { quizType === "idiom" || quizTypeLabel.includes("成语") || (fromUser === "星海小博士" && (content.includes("猜成语") || content.includes("猜谜活动"))) || - ((fromUser === "系统传音" || fromUser === "系统") && (content.includes("猜成语") || content.includes("猜谜活动"))) + (isSystemBroadcast && (content.includes("猜成语") || content.includes("猜谜活动"))) ) { return "猜成语"; } @@ -509,22 +510,68 @@ export function resolveBlockedSystemSenderKey(msg) { } // 兼容旧版自动钓鱼卡购买通知:历史上该消息曾以"系统传音"发送,但正文里带有"钓鱼播报"字样。 - if ((fromUser === "系统传音" || fromUser === "系统") && (content.includes("钓鱼播报") || content.includes("自动钓鱼模式"))) { + if (isSystemBroadcast && (content.includes("钓鱼播报") || content.includes("自动钓鱼模式"))) { return "钓鱼播报"; } - if ((fromUser === "系统传音" || fromUser === "系统") && content.includes("神秘箱子")) { + // 神秘箱子公告已精简为“《普通箱》...暗号...”等格式,不能再只依赖“神秘箱子”字样。 + if ( + isSystemBroadcast && ( + content.includes("神秘箱子") || + (content.includes("暗号") && content.includes("《")) || + content.includes("抢到普通箱") || + content.includes("抢到黑化箱") || + content.includes("抢到神秘") || + content.includes("箱子消失") + ) + ) { return "神秘箱子"; } - if ((fromUser === "系统传音" || fromUser === "系统") && content.includes("百家乐")) { + // 百家乐通知已缩短为“开局:...”“🎲 9点...”这类格式,这里同步兼容新旧文案。 + if ( + isSystemBroadcast && ( + content.includes("百家乐") || + (content.includes("开局:") && content.includes("点收割")) || + (content.startsWith("🎲") && content.includes("点")) || + (content.includes("快速参与") && content.includes("1:24")) + ) + ) { return "百家乐"; } - if ((fromUser === "系统传音" || fromUser === "系统") && (content.includes("赛马") || content.includes("跑马"))) { + // 赛马通知已缩短为“开赛:...”“比赛开始:...”“冠军:...”等格式。 + if ( + isSystemBroadcast && ( + content.includes("赛马") || + content.includes("跑马") || + content.startsWith("🐎 开赛:") || + content.startsWith("🏇 比赛开始:") || + content.startsWith("🏆 冠军:") + ) + ) { return "跑马"; } + if (isSystemBroadcast && (content.includes("【五子棋】") || content.includes("五子棋"))) { + return "五子棋"; + } + + if (isSystemBroadcast && (content.includes("老虎机") || content.includes("【老虎机大奖】"))) { + return "老虎机"; + } + + if ( + isSystemBroadcast && ( + content.includes("双色球") || + /购买\s+\d+\s*期/u.test(content) || + /\d+\s*期:\s*🔴/u.test(content) || + content.includes("超级期") + ) + ) { + return "双色球彩票"; + } + return null; } @@ -605,6 +652,9 @@ export function syncBlockedSystemSenderCheckboxes() { "block-sender-baccarat": "百家乐", "block-sender-horse-race": "跑马", "block-sender-mystery-box": "神秘箱子", + "block-sender-gomoku": "五子棋", + "block-sender-slot-machine": "老虎机", + "block-sender-lottery": "双色球彩票", }; Object.entries(checkboxMap).forEach(([id, sender]) => { diff --git a/resources/views/chat/partials/layout/input-bar.blade.php b/resources/views/chat/partials/layout/input-bar.blade.php index 63bf1c1..e243184 100644 --- a/resources/views/chat/partials/layout/input-bar.blade.php +++ b/resources/views/chat/partials/layout/input-bar.blade.php @@ -160,6 +160,21 @@ $welcomeMessages = [ 神秘箱子 + + +
diff --git a/resources/views/chat/partials/scripts.blade.php b/resources/views/chat/partials/scripts.blade.php index 0576f68..45b3b36 100644 --- a/resources/views/chat/partials/scripts.blade.php +++ b/resources/views/chat/partials/scripts.blade.php @@ -164,7 +164,7 @@ const CHAT_SOUND_MUTED_KEY = window.ChatRoomTools?.CHAT_SOUND_MUTED_STORAGE_KEY || 'chat_sound_muted'; const BLOCKED_SENDERS_KEY = window.ChatRoomTools?.BLOCKED_SYSTEM_SENDERS_STORAGE_KEY || 'chat_blocked_system_senders'; - const BLOCKABLE_SENDERS = window.ChatRoomTools?.BLOCKABLE_SYSTEM_SENDERS || ['钓鱼播报', '猜成语', '星海小博士', '百家乐', '跑马', '神秘箱子']; + const BLOCKABLE_SENDERS = window.ChatRoomTools?.BLOCKABLE_SYSTEM_SENDERS || ['钓鱼播报', '猜成语', '星海小博士', '百家乐', '跑马', '神秘箱子', '五子棋', '老虎机', '双色球彩票']; const initialChatPreferences = window.ChatRoomTools?.normalizeChatPreferences ? window.ChatRoomTools.normalizeChatPreferences(window.chatContext?.chatPreferences || {}, BLOCKABLE_SENDERS)