From d8aed495d9769477c96847a2d07004ccffc73f78 Mon Sep 17 00:00:00 2001 From: Megghy Date: Sat, 26 Apr 2025 04:15:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E9=98=9F=E5=88=97=E8=AE=BE=E7=BD=AE=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=87=AA=E5=8A=A8=E6=93=8D=E4=BD=9C=E4=BD=93?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 ClientAutoAction.vue 中新增消息队列设置标签页,允许用户配置弹幕和私信发送间隔。 - 更新多个组件以支持新的设置选项,提升用户交互体验。 - 在 useBiliFunction 中实现队列处理逻辑,确保消息按设定间隔发送。 - 优化 CheckInSettings 组件,整合 AutoActionEditor 以简化配置管理。 --- src/client/ClientAutoAction.vue | 54 +++- .../autoaction/AutoActionEditor.vue | 14 +- .../components/autoaction/TemplateEditor.vue | 1 - .../autoaction/settings/BasicSettings.vue | 10 + .../autoaction/settings/CheckInSettings.vue | 30 +-- src/client/data/initialize.ts | 2 + .../store/autoAction/modules/checkin.ts | 147 ++++------- src/client/store/useBiliFunction.ts | 236 ++++++++++++------ src/client/store/useSettings.ts | 11 + src/data/UpdateNote.ts | 22 ++ 10 files changed, 331 insertions(+), 196 deletions(-) diff --git a/src/client/ClientAutoAction.vue b/src/client/ClientAutoAction.vue index c0e72f8..d9345de 100644 --- a/src/client/ClientAutoAction.vue +++ b/src/client/ClientAutoAction.vue @@ -3,6 +3,7 @@ import { AutoActionItem, TriggerType, useAutoAction } from '@/client/store/useAu import { useDanmakuClient } from '@/store/useDanmakuClient'; import { useBiliCookie } from '@/client/store/useBiliCookie'; import { useWebFetcher } from '@/store/useWebFetcher'; +import { useBiliFunction } from '@/client/store/useBiliFunction'; import { NAlert, NButton, @@ -24,7 +25,8 @@ import { NIcon, NText, NTooltip, - NInput + NInput, + NInputNumber } from 'naive-ui'; import { computed, h, onMounted, onUnmounted, ref, watch, reactive } from 'vue'; import { ArrowUp24Regular, ArrowDown24Regular, Target24Filled, Edit16Regular } from '@vicons/fluent'; @@ -40,6 +42,7 @@ const message = useMessage(); const danmakuClient = useDanmakuClient(); const biliCookieStore = useBiliCookie(); const webFetcherStore = useWebFetcher(); +const biliFunc = useBiliFunction(); // 从 store 获取 enabledTriggerTypes const enabledTriggerTypes = computed(() => autoActionStore.enabledTriggerTypes); @@ -451,10 +454,6 @@ function confirmTest() { } showTestModal.value = false; } - -onMounted(() => { - autoActionStore.init(); -}); @@ -248,6 +249,7 @@ import { EventModel, EventDataTypes } from '@/api/api-models'; import { Info24Filled } from '@vicons/fluent'; import { computed, h, ref } from 'vue'; import type { UserCheckInData } from '@/client/store/autoAction/modules/checkin'; +import AutoActionEditor from '../AutoActionEditor.vue'; const autoActionStore = useAutoAction(); const config = autoActionStore.checkInModule.checkInConfig; diff --git a/src/client/data/initialize.ts b/src/client/data/initialize.ts index 1971c6d..b173916 100644 --- a/src/client/data/initialize.ts +++ b/src/client/data/initialize.ts @@ -22,6 +22,7 @@ import { relaunch } from '@tauri-apps/plugin-process'; import { useDanmakuWindow } from "../store/useDanmakuWindow"; import { getAllWebviewWindows } from "@tauri-apps/api/webviewWindow"; import { useAutoAction } from "../store/useAutoAction"; +import { useBiliFunction } from "../store/useBiliFunction"; const accountInfo = useAccount(); @@ -147,6 +148,7 @@ export async function initAll(isOnBoot: boolean) { } useAutoAction().init(); + useBiliFunction().init(); clientInited.value = true; } diff --git a/src/client/store/autoAction/modules/checkin.ts b/src/client/store/autoAction/modules/checkin.ts index e3f240e..f849ba5 100644 --- a/src/client/store/autoAction/modules/checkin.ts +++ b/src/client/store/autoAction/modules/checkin.ts @@ -2,11 +2,12 @@ import { ref, Ref, computed } from 'vue'; import { EventModel, EventDataTypes } from '@/api/api-models'; import { ActionType, AutoActionItem, RuntimeState, TriggerType, Priority, KeywordMatchType } from '../types'; import { usePointStore } from '@/store/usePointStore'; -import { processTemplate } from '../actionUtils'; +import { processTemplate, executeActions } from '../actionUtils'; import { buildExecutionContext } from '../utils'; import { v4 as uuidv4 } from 'uuid'; import { useIDBKeyval } from '@vueuse/integrations/useIDBKeyval'; import { GuidUtils } from '@/Utils'; +import { createDefaultAutoAction } from '../utils'; // 签到配置接口 export interface CheckInConfig { @@ -27,59 +28,33 @@ export interface CheckInConfig { } // 创建默认配置 -function createDefaultCheckInConfig(): CheckInConfig { return { +function createDefaultCheckInConfig(): CheckInConfig { + const successAction = createDefaultAutoAction(TriggerType.DANMAKU); + successAction.name = '签到成功回复'; + successAction.template = '@{{user.name}} 签到成功,获得 {{checkin.totalPoints}} 积分。'; + + const cooldownAction = createDefaultAutoAction(TriggerType.DANMAKU); + cooldownAction.name = '签到冷却回复'; + cooldownAction.template = '{{user.name}} 你今天已经签到过了,明天再来吧~'; + + const earlyBirdAction = createDefaultAutoAction(TriggerType.DANMAKU); + earlyBirdAction.name = '早鸟签到回复'; + earlyBirdAction.template = '恭喜 {{user.name}} 完成早鸟签到!额外获得 {{bonusPoints}} 积分,共获得 {{totalPoints}} 积分!'; + + return { enabled: false, command: '签到', points: 10, cooldownSeconds: 3600, // 1小时 onlyDuringLive: true, // 默认仅在直播时可签到 sendReply: true, // 默认发送回复消息 - successAction: { - id: uuidv4(), - name: '签到成功回复', - enabled: true, - triggerType: TriggerType.DANMAKU, - actionType: ActionType.SEND_DANMAKU, - template: '@{{user.name}} 签到成功,获得 {{checkin.totalPoints}} 积分。', - priority: Priority.NORMAL, - logicalExpression: '', - ignoreCooldown: false, - executeCommand: '', - triggerConfig: {}, - actionConfig: {} - }, - cooldownAction: { - id: uuidv4(), - name: '签到冷却回复', - enabled: true, - triggerType: TriggerType.DANMAKU, - actionType: ActionType.SEND_DANMAKU, - template: '{{user.name}} 你今天已经签到过了,明天再来吧~', - priority: Priority.NORMAL, - logicalExpression: '', - ignoreCooldown: false, - executeCommand: '', - triggerConfig: {}, - actionConfig: {} - }, + successAction, + cooldownAction, earlyBird: { enabled: false, windowMinutes: 30, bonusPoints: 5, - successAction: { - id: uuidv4(), - name: '早鸟签到回复', - enabled: true, - triggerType: TriggerType.DANMAKU, - actionType: ActionType.SEND_DANMAKU, - template: '恭喜 {{user.name}} 完成早鸟签到!额外获得 {{bonusPoints}} 积分,共获得 {{totalPoints}} 积分!', - priority: Priority.NORMAL, - logicalExpression: '', - ignoreCooldown: false, - executeCommand: '', - triggerConfig: {}, - actionConfig: {} - } + successAction: earlyBirdAction } }; } @@ -186,18 +161,22 @@ export function useCheckIn( if (lastCheckInTime > 0 && isSameDay) { // 用户今天已经签到过,发送提示 if (checkInConfig.value.sendReply) { - // 使用buildExecutionContext构建上下文 + // 构建上下文 const cooldownContext = buildExecutionContext(event, roomId.value, TriggerType.DANMAKU, { user: { name: username, uid: userId } }); - - const message = processTemplate(checkInConfig.value.cooldownAction, cooldownContext); - - if (roomId.value && message) { - sendDanmaku(roomId.value, message).catch(err => - console.error('[CheckIn] 发送已签到提示失败:', err) - ); - } + // 统一用 executeActions + executeActions( + [checkInConfig.value.cooldownAction], + event, + TriggerType.DANMAKU, + roomId.value, + runtimeState, + { sendLiveDanmaku: sendDanmaku }, + { + customContextBuilder: () => cooldownContext + } + ); } window.$notification.info({ title: '签到提示', @@ -227,21 +206,15 @@ export function useCheckIn( // 更新用户积分 try { // 调用积分系统添加积分 - const point = await pointStore.addPoints(userId, pointsEarned, `签到奖励 (${format(new Date(), 'yyyy-MM-dd')})`, `${username} 完成签到`); // 更新签到记录 + const point = await pointStore.addPoints(userId, pointsEarned, `签到奖励 (${format(new Date(), 'yyyy-MM-dd')})`, `${username} 完成签到`); if (checkInStorage.value) { - // 确保 lastCheckIn 对象存在 if (!checkInStorage.value.lastCheckIn) { checkInStorage.value.lastCheckIn = {}; } - // 确保 users 对象存在 if (!checkInStorage.value.users) { checkInStorage.value.users = {}; } - - // 获取用户当前的签到数据 let userData = checkInStorage.value.users[userId]; - - // 如果是新用户,创建用户数据 if (!userData) { userData = { ouid: userId, @@ -253,54 +226,31 @@ export function useCheckIn( firstCheckInTime: currentTime }; } - - // 计算连续签到天数 const lastCheckInDate = new Date(userData.lastCheckInTime); const currentDate = new Date(currentTime); - - // 如果上次签到不是昨天(隔了一天以上),则重置连续签到天数 const isYesterday = lastCheckInDate.getFullYear() === currentDate.getFullYear() && lastCheckInDate.getMonth() === currentDate.getMonth() && lastCheckInDate.getDate() === currentDate.getDate() - 1; - - // 如果上次签到不是今天(防止重复计算) if (!isSameDay) { - // 更新连续签到天数 if (isYesterday) { - // 昨天签到过,增加连续签到天数 userData.streakDays += 1; } else if (userData.lastCheckInTime > 0) { - // 不是昨天签到且不是首次签到,重置连续签到天数为1 userData.streakDays = 1; } else { - // 首次签到 userData.streakDays = 1; } - - // 更新累计签到次数 userData.totalCheckins += 1; - - // 更新早鸟签到次数 if (isEarlyBird) { userData.earlyBirdCount += 1; } } - - // 更新最后签到时间 userData.lastCheckInTime = currentTime; - // 更新用户名(以防用户改名) userData.username = username; - - // 保存用户数据 checkInStorage.value.users[userId] = userData; - // 更新lastCheckIn记录 checkInStorage.value.lastCheckIn[userId] = currentTime; } - - // 发送成功消息 if (roomId.value) { - // 构建签到上下文数据 const checkInData = { checkin: { points: checkInConfig.value.points, @@ -312,28 +262,21 @@ export function useCheckIn( cooldownSeconds: checkInConfig.value.cooldownSeconds } }; - - // 根据配置决定是否发送回复消息 if (checkInConfig.value.sendReply) { - // 使用buildExecutionContext构建完整上下文 const successContext = buildExecutionContext(event, roomId.value, TriggerType.DANMAKU, checkInData); - - let message; - if (isEarlyBird) { - // 使用早鸟签到模板 - message = processTemplate(checkInConfig.value.earlyBird.successAction, successContext); - } else { - // 使用普通签到模板 - message = processTemplate(checkInConfig.value.successAction, successContext); - } - - if (message) { - sendDanmaku(roomId.value, message).catch(err => - console.error('[CheckIn] 发送签到成功消息失败:', err) - ); - } + const action = isEarlyBird ? checkInConfig.value.earlyBird.successAction : checkInConfig.value.successAction; + executeActions( + [action], + event, + TriggerType.DANMAKU, + roomId.value, + runtimeState, + { sendLiveDanmaku: sendDanmaku }, + { + customContextBuilder: () => successContext + } + ); } - window.$notification.success({ title: '签到成功', description: `${username} 完成签到, 获得 ${pointsEarned} 积分, 累计签到 ${checkInStorage.value.users[userId].totalCheckins} 次`, diff --git a/src/client/store/useBiliFunction.ts b/src/client/store/useBiliFunction.ts index f3f50c4..d0000e0 100644 --- a/src/client/store/useBiliFunction.ts +++ b/src/client/store/useBiliFunction.ts @@ -2,10 +2,11 @@ import { useAccount } from "@/api/account"; import { useBiliCookie } from "./useBiliCookie"; import { fetch as tauriFetch } from "@tauri-apps/plugin-http"; // 引入 Body import { defineStore, acceptHMRUpdate } from 'pinia'; -import { computed, ref } from 'vue'; +import { computed, ref, onUnmounted } from 'vue'; import md5 from 'md5'; import { QueryBiliAPI } from "../data/utils"; import { onSendPrivateMessageFailed } from "../data/notification"; +import { useSettings } from "./useSettings"; // WBI 混合密钥编码表 const mixinKeyEncTab = [ @@ -79,27 +80,105 @@ async function getWbiKeys(cookie: string): Promise<{ img_key: string, sub_key: s export const useBiliFunction = defineStore('biliFunction', () => { const biliCookieStore = useBiliCookie(); const account = useAccount(); + const settingsStore = useSettings(); const cookie = computed(() => biliCookieStore.cookie); const uid = computed(() => account.value.biliId); // 存储WBI密钥 const wbiKeys = ref<{ img_key: string, sub_key: string } | null>(null); + // 队列相关状态 + const danmakuQueue = ref<{ roomId: number, message: string, color?: string, fontsize?: number, mode?: number }[]>([]); + const pmQueue = ref<{ receiverId: number, message: string }[]>([]); + const isDanmakuProcessing = ref(false); + const isPmProcessing = ref(false); + const danmakuTimer = ref | null>(null); + const pmTimer = ref | null>(null); + + // 使用computed获取设置中的间隔值 + const danmakuInterval = computed(() => settingsStore.settings.danmakuInterval); + const pmInterval = computed(() => settingsStore.settings.pmInterval); + const csrf = computed(() => { if (!cookie.value) return null; const match = cookie.value.match(/bili_jct=([^;]+)/); return match ? match[1] : null; }); - /** - * 发送直播弹幕 - * @param roomId 直播间 ID - * @param message 弹幕内容 - * @param color 弹幕颜色 (十六进制, 如 FFFFFF) - * @param fontsize 字体大小 (默认 25) - * @param mode 弹幕模式 (1: 滚动, 4: 底部, 5: 顶部) - * @returns Promise 是否发送成功 (基于API响应码) - */ - async function sendLiveDanmaku(roomId: number, message: string, color: string = 'ffffff', fontsize: number = 25, mode: number = 1): Promise { + // 设置间隔的方法 + async function setDanmakuInterval(interval: number) { + settingsStore.settings.danmakuInterval = interval; + await settingsStore.save(); + } + + async function setPmInterval(interval: number) { + settingsStore.settings.pmInterval = interval; + await settingsStore.save(); + } + + // 处理弹幕队列 + async function processDanmakuQueue() { + if (isDanmakuProcessing.value || danmakuQueue.value.length === 0) return; + isDanmakuProcessing.value = true; + console.log('[BiliFunction] 处理弹幕队列', danmakuQueue.value); + try { + const item = danmakuQueue.value[0]; + await _sendLiveDanmaku(item.roomId, item.message, item.color, item.fontsize, item.mode); + danmakuQueue.value.shift(); + } finally { + isDanmakuProcessing.value = false; + if (danmakuQueue.value.length > 0) { + danmakuTimer.value = setTimeout(() => processDanmakuQueue(), danmakuInterval.value); + } + } + } + + // 处理私信队列 + async function processPmQueue() { + if (isPmProcessing.value || pmQueue.value.length === 0) return; + isPmProcessing.value = true; + + try { + const item = pmQueue.value[0]; + await _sendPrivateMessage(item.receiverId, item.message); + pmQueue.value.shift(); + } finally { + isPmProcessing.value = false; + if (pmQueue.value.length > 0) { + pmTimer.value = setTimeout(() => processPmQueue(), pmInterval.value); + } + } + } + + // 清理定时器 + function clearTimers() { + if (danmakuTimer.value) { + clearTimeout(danmakuTimer.value); + danmakuTimer.value = null; + } + if (pmTimer.value) { + clearTimeout(pmTimer.value); + pmTimer.value = null; + } + } + + // 初始化函数 + async function init() { + // 确保设置已经初始化 + if (!settingsStore.settings.danmakuInterval) { + await settingsStore.init(); + } + // 启动队列处理 + processDanmakuQueue(); + processPmQueue(); + console.log('[BiliFunction] 队列初始化完成'); + // 清理定时器 + onUnmounted(() => { + clearTimers(); + }); + } + + // 原始发送弹幕方法(重命名为_sendLiveDanmaku) + async function _sendLiveDanmaku(roomId: number, message: string, color: string = 'ffffff', fontsize: number = 25, mode: number = 1): Promise { if (!csrf.value || !cookie.value) { console.error("发送弹幕失败:缺少 cookie 或 csrf token"); return false; @@ -124,7 +203,6 @@ export const useBiliFunction = defineStore('biliFunction', () => { }; const params = new URLSearchParams(data) try { - // 注意: B站网页版发送弹幕是用 application/x-www-form-urlencoded const response = await tauriFetch(url, { method: "POST", headers: { @@ -133,7 +211,7 @@ export const useBiliFunction = defineStore('biliFunction', () => { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36", "Referer": `https://live.bilibili.com/${roomId}` }, - body: params, // 发送 JSON 数据 + body: params, }); if (!response.ok) { @@ -141,7 +219,6 @@ export const useBiliFunction = defineStore('biliFunction', () => { return false; } const json = await response.json(); - // B站成功码通常是 0 if (json.code !== 0) { window.$notification.error({ title: '发送弹幕失败', @@ -168,65 +245,8 @@ export const useBiliFunction = defineStore('biliFunction', () => { } } - /** - * 封禁直播间用户 (需要主播或房管权限) - * @param roomId 直播间 ID - * @param userId 要封禁的用户 UID - * @param hours 封禁时长 (小时, 1-720) - */ - async function banLiveUser(roomId: number, userId: number, hours: number = 1) { - // 使用 csrf.value - if (!csrf.value || !cookie.value) { - console.error("封禁用户失败:缺少 cookie 或 csrf token"); - return; - } - // 确保 hours 在 1 到 720 之间 - const validHours = Math.max(1, Math.min(hours, 720)); - const url = "https://api.live.bilibili.com/banned_service/v2/Silent/add_user"; - const data = { - room_id: roomId.toString(), - block_uid: userId.toString(), - hour: validHours.toString(), - csrf: csrf.value, // 使用计算属性的值 - csrf_token: csrf.value, // 使用计算属性的值 - visit_id: "", // 通常可以为空 - }; - - try { - const params = new URLSearchParams(data) - const response = await tauriFetch(url, { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - "Cookie": cookie.value, // 使用计算属性的值 - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36", - "Referer": `https://live.bilibili.com/p/html/live-room-setting/#/room-manager/black-list?room_id=${roomId}` // 模拟来源 - }, - body: params, // 发送 URLSearchParams 数据 - }); - if (!response.ok) { - console.error("封禁用户失败:", response.status, await response.text()); - return response.statusText; - } - const json = await response.json(); - if (json.code !== 0) { - console.error("封禁用户API失败:", json.code, json.message || json.msg); - return json.data; - } - console.log("封禁用户成功:", json.data); - return json.data; - } catch (error) { - console.error("封禁用户时发生错误:", error); - } - } - - /** - * 发送私信 - * @param receiverId 接收者 UID - * @param message 私信内容 - * @returns Promise 是否发送成功 (基于API响应码) - */ - async function sendPrivateMessage(receiverId: number, message: string): Promise { + // 原始发送私信方法(重命名为_sendPrivateMessage) + async function _sendPrivateMessage(receiverId: number, message: string): Promise { if (!csrf.value || !cookie.value || !uid.value) { const error = "发送私信失败:缺少 cookie, csrf token 或 uid"; console.error(error); @@ -338,12 +358,82 @@ export const useBiliFunction = defineStore('biliFunction', () => { } } + // 新的队列发送方法 + async function sendLiveDanmaku(roomId: number, message: string, color: string = 'ffffff', fontsize: number = 25, mode: number = 1): Promise { + danmakuQueue.value.push({ roomId, message, color, fontsize, mode }); + processDanmakuQueue(); + return true; + } + + async function sendPrivateMessage(receiverId: number, message: string): Promise { + pmQueue.value.push({ receiverId, message }); + processPmQueue(); + return true; + } + + /** + * 封禁直播间用户 (需要主播或房管权限) + * @param roomId 直播间 ID + * @param userId 要封禁的用户 UID + * @param hours 封禁时长 (小时, 1-720) + */ + async function banLiveUser(roomId: number, userId: number, hours: number = 1) { + // 使用 csrf.value + if (!csrf.value || !cookie.value) { + console.error("封禁用户失败:缺少 cookie 或 csrf token"); + return; + } + // 确保 hours 在 1 到 720 之间 + const validHours = Math.max(1, Math.min(hours, 720)); + const url = "https://api.live.bilibili.com/banned_service/v2/Silent/add_user"; + const data = { + room_id: roomId.toString(), + block_uid: userId.toString(), + hour: validHours.toString(), + csrf: csrf.value, // 使用计算属性的值 + csrf_token: csrf.value, // 使用计算属性的值 + visit_id: "", // 通常可以为空 + }; + + try { + const params = new URLSearchParams(data) + const response = await tauriFetch(url, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + "Cookie": cookie.value, // 使用计算属性的值 + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36", + "Referer": `https://live.bilibili.com/p/html/live-room-setting/#/room-manager/black-list?room_id=${roomId}` // 模拟来源 + }, + body: params, // 发送 URLSearchParams 数据 + }); + if (!response.ok) { + console.error("封禁用户失败:", response.status, await response.text()); + return response.statusText; + } + const json = await response.json(); + if (json.code !== 0) { + console.error("封禁用户API失败:", json.code, json.message || json.msg); + return json.data; + } + console.log("封禁用户成功:", json.data); + return json.data; + } catch (error) { + console.error("封禁用户时发生错误:", error); + } + } + return { + init, sendLiveDanmaku, banLiveUser, sendPrivateMessage, csrf, uid, + danmakuInterval, + pmInterval, + setDanmakuInterval, + setPmInterval, }; }); diff --git a/src/client/store/useSettings.ts b/src/client/store/useSettings.ts index 838e95f..96dcae1 100644 --- a/src/client/store/useSettings.ts +++ b/src/client/store/useSettings.ts @@ -15,6 +15,10 @@ export type VTsuruClientSettings = { enableNotification: boolean; notificationSettings: NotificationSettings; + // 消息队列间隔设置 + danmakuInterval: number; + pmInterval: number; + dev_disableDanmakuClient: boolean; }; @@ -32,6 +36,10 @@ export const useSettings = defineStore('settings', () => { enableTypes: ['question-box', 'danmaku', 'message-failed'], }, + // 默认间隔为2秒 + danmakuInterval: 2000, + pmInterval: 2000, + dev_disableDanmakuClient: false, }; const settings = ref(Object.assign({}, defaultSettings)); @@ -40,6 +48,9 @@ export const useSettings = defineStore('settings', () => { settings.value = (await store.get()) || Object.assign({}, defaultSettings); settings.value.notificationSettings ??= defaultSettings.notificationSettings; settings.value.notificationSettings.enableTypes ??= [ 'question-box', 'danmaku', 'message-failed' ]; + // 初始化消息队列间隔设置 + settings.value.danmakuInterval ??= defaultSettings.danmakuInterval; + settings.value.pmInterval ??= defaultSettings.pmInterval; } async function save() { await store.set(settings.value); diff --git a/src/data/UpdateNote.ts b/src/data/UpdateNote.ts index bdcdee0..eb53033 100644 --- a/src/data/UpdateNote.ts +++ b/src/data/UpdateNote.ts @@ -4,6 +4,28 @@ import { VNode } from "vue"; import { FETCH_API } from "./constants"; export const updateNotes: updateNoteType[] = [ + { + ver: 6, + date: '2025.4.26', + items: [ + { + type: 'new', + title: '自动操作新增弹幕签到功能', + content: [ + [ + '客户端新增弹幕签到功能,支持观众通过发送特定指令获得积分 (需扫码登录或者使用CookieCloud才能发送回复', + () => h(NImage, { src: 'https://pan.suki.club/d/vtsuru/imgur/0e784480a3016b748af2579b2c492a3b.png', width: 300 }), + ], + [ + '客户端安装方式:', + () => h(NButton, { + text: true, tag: 'a', href: 'https://www.wolai.com/carN6qvUm3FErze9Xo53ii', target: '_blank', type: 'info' + }, () => '查看介绍'), + ] + ], + }, + ] + }, { ver: 5, date: '2025.4.24',