Feat/complete adapter features (#1849)

* feat: add voice and file supports for wecom

* feat: add   and  in query variables

* feat: supports for lark recv file message

* feat: kook recv voice msg

* feat: supports for Voice and File in discord

* chore: remove debug msg

* perf: remove unnecessary bot logs

* feat: implement bot log filtering and per label color (#1839)

* feat: add sender_name and group_name in query variables
This commit is contained in:
Junyan Qin (Chin)
2025-12-06 21:11:01 +08:00
committed by GitHub
parent daf56e5dc2
commit 6421a6f5cb
22 changed files with 464 additions and 145 deletions

View File

@@ -44,12 +44,35 @@ export function BotLogCard({ botLog }: { botLog: BotLog }) {
const strArr = str.split('');
return strArr;
}
// 根据日志级别返回对应的样式类
function getLevelStyles(level: string) {
switch (level.toLowerCase()) {
case 'error':
return 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400';
case 'warning':
return 'bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-400';
case 'info':
return 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400';
case 'debug':
return 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-400';
default:
return 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-400';
}
}
return (
<div className={`${styles.botLogCardContainer}`}>
{/* 头部标签,时间 */}
<div className={`${styles.cardTitleContainer}`}>
<div className={`flex flex-row gap-2 items-center`}>
<div className={`${styles.tag}`}>{botLog.level}</div>
<div
className={`px-2 py-1 rounded text-xs font-medium uppercase ${getLevelStyles(
botLog.level,
)}`}
>
{botLog.level}
</div>
{botLog.message_session_id && (
<div
className={`${styles.tag} ${styles.chatTag}`}

View File

@@ -1,11 +1,19 @@
'use client';
import { BotLogManager } from '@/app/home/bots/components/bot-log/BotLogManager';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useRef, useState, useMemo } from 'react';
import { BotLog } from '@/app/infra/http/requestParam/bots/GetBotLogsResponse';
import { BotLogCard } from '@/app/home/bots/components/bot-log/view/BotLogCard';
import styles from './botLog.module.css';
import { Switch } from '@/components/ui/switch';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { ChevronDownIcon } from 'lucide-react';
import { debounce } from 'lodash';
import { useTranslation } from 'react-i18next';
@@ -14,9 +22,21 @@ export function BotLogListComponent({ botId }: { botId: string }) {
const manager = useRef(new BotLogManager(botId)).current;
const [botLogList, setBotLogList] = useState<BotLog[]>([]);
const [autoFlush, setAutoFlush] = useState(true);
const [selectedLevels, setSelectedLevels] = useState<string[]>([
'info',
'warning',
'error',
]);
const listContainerRef = useRef<HTMLDivElement>(null);
const botLogListRef = useRef<BotLog[]>(botLogList);
const logLevels = [
{ value: 'error', label: 'ERROR' },
{ value: 'warning', label: 'WARNING' },
{ value: 'info', label: 'INFO' },
{ value: 'debug', label: 'DEBUG' },
];
useEffect(() => {
initComponent();
return () => {
@@ -28,6 +48,42 @@ export function BotLogListComponent({ botId }: { botId: string }) {
botLogListRef.current = botLogList;
}, [botLogList]);
// 根据级别过滤日志
const filteredLogs = useMemo(() => {
if (selectedLevels.length === 0) {
return botLogList;
}
return botLogList.filter((log) => selectedLevels.includes(log.level));
}, [botLogList, selectedLevels]);
const handleLevelToggle = (levelValue: string) => {
setSelectedLevels((prev) => {
if (prev.includes(levelValue)) {
return prev.filter((l) => l !== levelValue);
} else {
return [...prev, levelValue];
}
});
};
const getDisplayText = () => {
if (selectedLevels.length === 0) {
return t('bots.selectLevel');
}
if (selectedLevels.length === logLevels.length) {
return t('bots.allLevels');
}
// 如果选中3个或以上显示数量
if (selectedLevels.length >= 3) {
return `${selectedLevels.length} ${t('bots.levelsSelected')}`;
}
// 显示选中级别的标签(大写形式)
return logLevels
.filter((level) => selectedLevels.includes(level.value))
.map((level) => level.label)
.join(', ');
};
// 观测自动刷新状态
useEffect(() => {
if (autoFlush) {
@@ -116,9 +172,43 @@ export function BotLogListComponent({ botId }: { botId: string }) {
<div className={`${styles.listHeader}`}>
<div className={'mr-2'}>{t('bots.enableAutoRefresh')}</div>
<Switch checked={autoFlush} onCheckedChange={(e) => setAutoFlush(e)} />
<div className={'ml-4 mr-2'}>{t('bots.logLevel')}</div>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
size="sm"
className="w-[180px] flex items-center justify-between"
>
<span className="text-sm truncate flex-1 text-left">
{getDisplayText()}
</span>
<ChevronDownIcon className="ml-2 h-4 w-4 flex-shrink-0" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[180px] p-2">
<div className="flex flex-col gap-2">
{logLevels.map((level) => (
<div key={level.value} className="flex items-center space-x-2">
<Checkbox
id={level.value}
checked={selectedLevels.includes(level.value)}
onCheckedChange={() => handleLevelToggle(level.value)}
/>
<label
htmlFor={level.value}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer"
>
{level.label}
</label>
</div>
))}
</div>
</PopoverContent>
</Popover>
</div>
{botLogList.map((botLog) => {
{filteredLogs.map((botLog) => {
return <BotLogCard botLog={botLog} key={botLog.seq_id} />;
})}
</div>

View File

@@ -192,6 +192,10 @@ const enUS = {
webhookUrlCopied: 'Webhook URL copied',
webhookUrlHint:
'Click the input to select all, then press Ctrl+C (Mac: Cmd+C) to copy, or click the button',
logLevel: 'Log Level',
allLevels: 'All Levels',
selectLevel: 'Select Level',
levelsSelected: 'levels selected',
},
plugins: {
title: 'Extensions',

View File

@@ -194,6 +194,10 @@ const jaJP = {
webhookUrlCopied: 'Webhook URL をコピーしました',
webhookUrlHint:
'入力ボックスをクリックして全選択し、Ctrl+C (Mac: Cmd+C) でコピーするか、右側のボタンをクリックしてください',
logLevel: 'ログレベル',
allLevels: 'すべてのレベル',
selectLevel: 'レベルを選択',
levelsSelected: 'レベル選択済み',
},
plugins: {
title: '拡張機能',

View File

@@ -187,6 +187,10 @@ const zhHans = {
webhookUrlCopied: 'Webhook 地址已复制',
webhookUrlHint:
'点击输入框自动全选,然后按 Ctrl+C (Mac: Cmd+C) 复制,或点击右侧按钮',
logLevel: '日志级别',
allLevels: '全部级别',
selectLevel: '选择级别',
levelsSelected: '个级别已选',
},
plugins: {
title: '插件扩展',

View File

@@ -187,6 +187,10 @@ const zhHant = {
webhookUrlCopied: 'Webhook 位址已複製',
webhookUrlHint:
'點擊輸入框自動全選,然後按 Ctrl+C (Mac: Cmd+C) 複製,或點擊右側按鈕',
logLevel: '日誌級別',
allLevels: '全部級別',
selectLevel: '選擇級別',
levelsSelected: '個級別已選',
},
plugins: {
title: '外掛擴展',