import React, { useState, useEffect, useMemo } from 'react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Switch } from '@/components/ui/switch' import { Slider } from '@/components/ui/slider' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '@/components/ui/alert-dialog' import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover' import { Plus, Trash2, Eye, Clock } from 'lucide-react' import type { FieldHookComponent } from '@/lib/field-hooks' import type { ChatConfig } from '../types' // 时间选择组件 const TimeRangePicker = React.memo(function TimeRangePicker({ value, onChange, }: { value: string onChange: (value: string) => void }) { // 解析初始值 const parsedValue = useMemo(() => { const parts = value.split('-') if (parts.length === 2) { const [start, end] = parts const [sh, sm] = start.split(':') const [eh, em] = end.split(':') return { startHour: sh ? sh.padStart(2, '0') : '00', startMinute: sm ? sm.padStart(2, '0') : '00', endHour: eh ? eh.padStart(2, '0') : '23', endMinute: em ? em.padStart(2, '0') : '59', } } return { startHour: '00', startMinute: '00', endHour: '23', endMinute: '59', } }, [value]) const [startHour, setStartHour] = useState(parsedValue.startHour) const [startMinute, setStartMinute] = useState(parsedValue.startMinute) const [endHour, setEndHour] = useState(parsedValue.endHour) const [endMinute, setEndMinute] = useState(parsedValue.endMinute) // 当value变化时同步状态 useEffect(() => { setStartHour(parsedValue.startHour) setStartMinute(parsedValue.startMinute) setEndHour(parsedValue.endHour) setEndMinute(parsedValue.endMinute) }, [parsedValue]) const updateTime = ( newStartHour: string, newStartMinute: string, newEndHour: string, newEndMinute: string ) => { const newValue = `${newStartHour}:${newStartMinute}-${newEndHour}:${newEndMinute}` onChange(newValue) } return (

开始时间

结束时间

) }) // 预览窗口组件 const RulePreview = React.memo(function RulePreview({ rule }: { rule: { target: string; time: string; value: number } }) { const previewText = `{ target = "${rule.target}", time = "${rule.time}", value = ${rule.value.toFixed(1)} }` return (

配置预览

{previewText}

这是保存到 bot_config.toml 文件中的格式

) }) /** * ChatSection as a Field Hook Component * This component replaces the entire 'chat' nested config section rendering */ export const ChatSectionHook: FieldHookComponent = ({ value, onChange }) => { // Cast value to ChatConfig (assuming it's the entire chat config object) const config = value as ChatConfig // Helper to update config const updateConfig = (updates: Partial) => { if (onChange) { onChange({ ...config, ...updates }) } } // 添加发言频率规则 const addTalkValueRule = () => { updateConfig({ talk_value_rules: [ ...config.talk_value_rules, { target: '', time: '00:00-23:59', value: 1.0 }, ], }) } // 删除发言频率规则 const removeTalkValueRule = (index: number) => { updateConfig({ talk_value_rules: config.talk_value_rules.filter((_, i) => i !== index), }) } // 更新发言频率规则 const updateTalkValueRule = ( index: number, field: 'target' | 'time' | 'value', value: string | number ) => { const newRules = [...config.talk_value_rules] newRules[index] = { ...newRules[index], [field]: value, } updateConfig({ talk_value_rules: newRules, }) } return (

聊天设置

updateConfig({ talk_value: parseFloat(e.target.value) })} />

越小越沉默,范围 0-1

控制麦麦的思考深度。经典模式回复快但简单;深度模式更深入但较慢;动态模式根据情况自动选择

updateConfig({ mentioned_bot_reply: checked }) } />
updateConfig({ max_context_size: parseInt(e.target.value) }) } />
updateConfig({ planner_smooth: parseFloat(e.target.value) }) } />

增大数值会减小 planner 负荷,推荐 1-5,0 为关闭

updateConfig({ plan_reply_log_max_per_chat: parseInt(e.target.value) }) } />

每个聊天流保存的 Plan/Reply 日志最大数量,超过此数量时会自动删除最老的日志

updateConfig({ llm_quote: checked }) } />

启用后,LLM 可以决定是否在回复时引用消息

updateConfig({ enable_talk_value_rules: checked }) } />
{/* 动态发言频率规则配置 */} {config.enable_talk_value_rules && (

动态发言频率规则

按时段或聊天流ID调整发言频率,优先匹配具体聊天,再匹配全局规则

{config.talk_value_rules && config.talk_value_rules.length > 0 ? (
{config.talk_value_rules.map((rule, index) => (
规则 #{index + 1}
确认删除 确定要删除规则 #{index + 1} 吗?此操作无法撤销。 取消 removeTalkValueRule(index)}> 删除
{/* 配置类型选择 */}
{/* 详细配置选项 - 只在非全局时显示 */} {rule.target !== '' && (() => { const parts = rule.target.split(':') const platform = parts[0] || 'qq' const chatId = parts[1] || '' const chatType = parts[2] || 'group' return (
{ updateTalkValueRule(index, 'target', `${platform}:${e.target.value}:${chatType}`) }} placeholder="输入群 ID" className="font-mono text-sm" />

当前聊天流 ID:{rule.target || '(未设置)'}

) })()} {/* 时间段选择器 */}
updateTalkValueRule(index, 'time', v)} />

支持跨夜区间,例如 23:00-02:00

{/* 发言频率滑块 */}
{ const val = parseFloat(e.target.value) if (!isNaN(val)) { updateTalkValueRule(index, 'value', Math.max(0.01, Math.min(1, val))) } }} className="w-20 h-8 text-xs" />
updateTalkValueRule(index, 'value', values[0]) } min={0.01} max={1} step={0.01} className="w-full" />
0.01 (极少发言) 0.5 1.0 (正常)
))}
) : (

暂无规则,点击"添加规则"按钮创建

)}
📝 规则说明
  • Target 为空:全局规则,对所有聊天生效
  • Target 指定:仅对特定聊天流生效(格式:platform:id:type)
  • 优先级:先匹配具体聊天流规则,再匹配全局规则
  • 时间支持跨夜:例如 23:00-02:00 表示晚上11点到次日凌晨2点
  • 数值范围:建议 0-1,0 表示完全沉默,1 表示正常发言
)}
) }