feat: 在 ClientAutoAction 组件中新增操作历史标签页和相关功能

- 引入 ActionHistoryViewer 组件,展示执行历史。
- 更新主标签页逻辑,调整为操作管理和执行历史两个标签。
- 在自动操作逻辑中增加弹幕和私信发送历史记录功能,提升操作追踪能力。
This commit is contained in:
2025-04-22 19:08:31 +08:00
parent d6577ec129
commit b97081a870
4 changed files with 695 additions and 170 deletions

View File

@@ -11,6 +11,7 @@ import { buildExecutionContext, getRandomTemplate } from './utils';
import { evaluateTemplateExpressions } from './expressionEvaluator';
import { evaluateExpression } from './utils';
import { useBiliCookie } from '../useBiliCookie';
import { logDanmakuHistory, logPrivateMsgHistory, logCommandHistory } from './utils/historyLogger';
/**
* 过滤有效的自动操作项
@@ -223,11 +224,57 @@ export function executeActions(
if (action.actionConfig.delaySeconds && action.actionConfig.delaySeconds > 0) {
setTimeout(() => {
handlers.sendLiveDanmaku!(roomId, message)
.catch(err => console.error(`[AutoAction] 发送弹幕失败 (${action.name || action.id}):`, err));
.then(success => {
// 记录弹幕发送历史
logDanmakuHistory(
action.id,
action.name || '未命名操作',
message,
roomId,
success,
success ? undefined : '发送失败'
).catch(err => console.error('记录弹幕历史失败:', err));
return success;
})
.catch(err => {
console.error(`[AutoAction] 发送弹幕失败 (${action.name || action.id}):`, err);
// 记录失败的发送
logDanmakuHistory(
action.id,
action.name || '未命名操作',
message,
roomId,
false,
err.toString()
).catch(e => console.error('记录弹幕历史失败:', e));
});
}, action.actionConfig.delaySeconds * 1000);
} else {
handlers.sendLiveDanmaku(roomId, message)
.catch(err => console.error(`[AutoAction] 发送弹幕失败 (${action.name || action.id}):`, err));
.then(success => {
// 记录弹幕发送历史
logDanmakuHistory(
action.id,
action.name || '未命名操作',
message,
roomId,
success,
success ? undefined : '发送失败'
).catch(err => console.error('记录弹幕历史失败:', err));
return success;
})
.catch(err => {
console.error(`[AutoAction] 发送弹幕失败 (${action.name || action.id}):`, err);
// 记录失败的发送
logDanmakuHistory(
action.id,
action.name || '未命名操作',
message,
roomId,
false,
err.toString()
).catch(e => console.error('记录弹幕历史失败:', e));
});
}
}
} else {
@@ -249,6 +296,16 @@ export function executeActions(
const sendPmPromise = (uid: number, msg: string) => {
return handlers.sendPrivateMessage!(uid, msg)
.then(success => {
// 记录私信发送历史
logPrivateMsgHistory(
action.id,
action.name || '未命名操作',
msg,
uid,
success,
success ? undefined : '发送失败'
).catch(err => console.error('记录私信历史失败:', err));
if (success && options?.onSuccess) {
// 发送成功后调用 onSuccess 回调
options.onSuccess(action, context);
@@ -257,6 +314,15 @@ export function executeActions(
})
.catch(err => {
console.error(`[AutoAction] 发送私信失败 (${action.name || action.id}):`, err);
// 记录失败的发送
logPrivateMsgHistory(
action.id,
action.name || '未命名操作',
msg,
uid,
false,
err.toString()
).catch(e => console.error('记录私信历史失败:', e));
return false; // 明确返回 false 表示失败
});
};
@@ -276,8 +342,22 @@ export function executeActions(
break;
case ActionType.EXECUTE_COMMAND:
// 执行自定义命令(未实现)
console.warn(`[AutoAction] 暂不支持执行自定义命令: ${action.name || action.id}`);
// 执行自定义命令
const command = processTemplate(action, context);
if (command) {
// 更新冷却时间
runtimeState.lastExecutionTime[action.id] = Date.now();
// 目前只记录执行历史,具体实现可在未来扩展
logCommandHistory(
action.id,
action.name || '未命名操作',
command,
true
).catch(err => console.error('记录命令执行历史失败:', err));
console.warn(`[AutoAction] 暂不支持执行自定义命令: ${action.name || action.id}`);
}
break;
default:

View File

@@ -0,0 +1,145 @@
import { get, set, del, update } from 'idb-keyval';
import { ActionType } from '../types';
// 历史记录类型常量
export enum HistoryType {
DANMAKU = 'danmaku',
PRIVATE_MSG = 'privateMsg',
COMMAND = 'command',
}
// 历史记录项结构
export interface HistoryItem {
id: string; // 唯一ID
actionId: string; // 操作ID
actionName: string; // 操作名称
actionType: ActionType; // 操作类型
timestamp: number; // 执行时间戳
content: string; // 发送的内容
target?: string; // 目标如UID或房间ID
success: boolean; // 是否成功
error?: string; // 错误信息(如果有)
}
// 每种类型的历史记录容量
const HISTORY_CAPACITY = 1000;
// 使用IDB存储的键名
const HISTORY_KEYS = {
[HistoryType.DANMAKU]: 'autoAction_history_danmaku',
[HistoryType.PRIVATE_MSG]: 'autoAction_history_privateMsg',
[HistoryType.COMMAND]: 'autoAction_history_command',
};
// 环形队列添加记录
async function addToCircularQueue(key: string, item: HistoryItem, capacity: number): Promise<void> {
await update<HistoryItem[]>(key, (history = []) => {
// 添加到队列末尾
history.push(item);
// 如果超出容量,移除最旧的记录
if (history.length > capacity) {
history.splice(0, history.length - capacity);
}
return history;
});
}
/**
* 记录弹幕发送历史
*/
export async function logDanmakuHistory(
actionId: string,
actionName: string,
content: string,
roomId: number,
success: boolean,
error?: string
): Promise<void> {
const historyItem: HistoryItem = {
id: `d_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`,
actionId,
actionName,
actionType: ActionType.SEND_DANMAKU,
timestamp: Date.now(),
content,
target: roomId.toString(),
success,
error
};
await addToCircularQueue(HISTORY_KEYS[HistoryType.DANMAKU], historyItem, HISTORY_CAPACITY);
}
/**
* 记录私信发送历史
*/
export async function logPrivateMsgHistory(
actionId: string,
actionName: string,
content: string,
userId: number,
success: boolean,
error?: string
): Promise<void> {
const historyItem: HistoryItem = {
id: `p_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`,
actionId,
actionName,
actionType: ActionType.SEND_PRIVATE_MSG,
timestamp: Date.now(),
content,
target: userId.toString(),
success,
error
};
await addToCircularQueue(HISTORY_KEYS[HistoryType.PRIVATE_MSG], historyItem, HISTORY_CAPACITY);
}
/**
* 记录命令执行历史
*/
export async function logCommandHistory(
actionId: string,
actionName: string,
content: string,
success: boolean,
error?: string
): Promise<void> {
const historyItem: HistoryItem = {
id: `c_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`,
actionId,
actionName,
actionType: ActionType.EXECUTE_COMMAND,
timestamp: Date.now(),
content,
success,
error
};
await addToCircularQueue(HISTORY_KEYS[HistoryType.COMMAND], historyItem, HISTORY_CAPACITY);
}
/**
* 获取历史记录
*/
export async function getHistoryByType(type: HistoryType): Promise<HistoryItem[]> {
return await get<HistoryItem[]>(HISTORY_KEYS[type]) || [];
}
/**
* 清除历史记录
*/
export async function clearHistory(type: HistoryType): Promise<void> {
await del(HISTORY_KEYS[type]);
}
/**
* 清除所有历史记录
*/
export async function clearAllHistory(): Promise<void> {
await Promise.all(
Object.values(HistoryType).map(type => clearHistory(type as HistoryType))
);
}