Files
ops-assistant/internal/core/ops/service.go
T
openclaw 81939825d1 feat: 模块化命令框架 + runbook 严格断言与查询结果回传
- core/module: 命令模板、门控(feature flag/confirm/dry-run)、ecode 错误标签、ResultFormatter
- runbook executor: http.get/http.post/assert.json(expect_success 严格断言)、目标主机管理、任务落库、最终 stdout 直接回传
- cf 模块: /cf dns list 支持域名自动解析 zone_id;dns list/zones/workers list 格式化回传;API success:false 判真失败
- 全部 CF/CPA runbook 接入严格断言,杜绝假阳性 success
- 新增 cpa/cfdav/mail/sub 模块与 runbooks;QQ 订阅到期提醒调度
- Web 后台: 模块设置(CF/CPA/cfdav/AI)、OPS 任务、渠道管理、审计页
2026-09-13 07:42:17 +08:00

93 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package ops
import (
"fmt"
"strings"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"ops-assistant/internal/core/command"
coremodule "ops-assistant/internal/core/module"
"ops-assistant/internal/core/registry"
)
type Service struct {
dbPath string
baseDir string
registry *registry.Registry
db *gorm.DB
}
func NewService(dbPath, baseDir string, reg *registry.Registry) *Service {
db, _ := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
return &Service{dbPath: dbPath, baseDir: baseDir, registry: reg, db: db}
}
func (s *Service) Handle(userID int64, text string) (bool, string) {
if !strings.HasPrefix(strings.TrimSpace(text), "/") {
return false, ""
}
cmd, _, err := command.ParseWithInputs(text)
if err != nil {
return false, ""
}
// 通用帮助
if cmd.Module == "help" || cmd.Name == "/help" || cmd.Name == "/start" {
return true, s.helpText()
}
if cmd.Module == "ops" && (len(cmd.Args) == 0 || cmd.Args[0] == "help") {
return true, s.helpText()
}
if cmd.Module == "ops" && len(cmd.Args) > 0 && cmd.Args[0] == "modules" {
return true, s.modulesStatusText()
}
if cmd.Module != "" && cmd.Module != "ops" && s.db != nil {
if !coremodule.IsEnabled(s.db, cmd.Module) {
return true, fmt.Sprintf("[ERR_FEATURE_DISABLED] 模块未启用: %s(开关: enable_module_%s)", cmd.Module, cmd.Module)
}
}
out, handled, err := s.registry.Handle(userID, cmd)
if !handled {
return false, ""
}
if err != nil {
return true, "❌ OPS 执行失败: " + err.Error()
}
return true, out
}
func (s *Service) helpText() string {
lines := []string{
"🛠️ OPS 帮助(模块入口):",
"- /ops modules (查看模块启用状态)",
"- /cpa (CPA 使用统计/备份/恢复)",
"- /cf (Cloudflare 管理)",
"- /mail (邮箱状态)",
"- /cfdav (CFDAV 用户管理)",
"- /sub (订阅管理)",
"提示:发送 /cpa /cf /mail /cfdav /sub 获取各模块完整指令清单。",
}
return strings.Join(lines, "\n")
}
func (s *Service) modulesStatusText() string {
mods := s.registry.ListModules()
if len(mods) == 0 {
return "暂无已注册模块"
}
lines := []string{"🧩 模块状态:"}
for _, m := range mods {
enabled := false
if s.db != nil {
enabled = coremodule.IsEnabled(s.db, m)
}
state := "disabled"
if enabled {
state = "enabled"
}
lines = append(lines, fmt.Sprintf("- %s: %s", m, state))
}
lines = append(lines, "\n可用命令:/ops modules")
return strings.Join(lines, "\n")
}