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();
-});
@@ -723,6 +722,51 @@ onMounted(() => {
>
+
+
+
+
+
+
+ 弹幕队列间隔(毫秒):
+ biliFunc.setDanmakuInterval(Number(v))"
+ />
+
+ 每{{ biliFunc.danmakuInterval }}ms 发送一条弹幕
+
+
+
+ 私信队列间隔(毫秒):
+ biliFunc.setPmInterval(Number(v))"
+ />
+
+ 每{{ biliFunc.pmInterval }}ms 发送一条私信
+
+
+
+
+
diff --git a/src/client/components/autoaction/AutoActionEditor.vue b/src/client/components/autoaction/AutoActionEditor.vue
index 9f29444..d273cff 100644
--- a/src/client/components/autoaction/AutoActionEditor.vue
+++ b/src/client/components/autoaction/AutoActionEditor.vue
@@ -18,6 +18,14 @@ const props = defineProps({
action: {
type: Object as () => AutoActionItem,
required: true
+ },
+ hideName: {
+ type: Boolean,
+ default: false
+ },
+ hideEnabled: {
+ type: Boolean,
+ default: false
}
});
@@ -53,7 +61,11 @@ const TriggerSettings = getTriggerSettings();
-
+
diff --git a/src/client/components/autoaction/TemplateEditor.vue b/src/client/components/autoaction/TemplateEditor.vue
index ad4d07d..994e06f 100644
--- a/src/client/components/autoaction/TemplateEditor.vue
+++ b/src/client/components/autoaction/TemplateEditor.vue
@@ -398,7 +398,6 @@ function insertExample(template: string) {
AutoActionItem,
required: true
+ },
+ hideName: {
+ type: Boolean,
+ default: false
+ },
+ hideEnabled: {
+ type: Boolean,
+ default: false
}
});
@@ -44,6 +52,7 @@ const priorityOptions = [
class="basic-settings"
>
- 每个用户在指定秒数内只能签到一次。
+ 每个用户在指定秒数内签到命令只会响应一次
@@ -90,15 +90,16 @@
-
+
-
@@ -133,11 +134,11 @@
成功触发早鸟签到的用户额外获得的积分。
-
+
@@ -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',