package util import ( "context" "fmt" "html" "strings" "github.com/go-telegram/bot" "github.com/go-telegram/bot/models" "github.com/tiennm99/miti99bot/internal/modules" ) const repoURL = "https://github.com/tiennm99/miti99bot" var supportFooter = fmt.Sprintf( `Enjoying the bot? Support me by starring the repo: %s`, repoURL, repoURL, ) // RenderHelp produces the body of /help: each module's public commands // grouped under a bold module name, followed by the support footer. // Modules in MODULES-env order. Modules with no visible commands are omitted. // Protected and private commands are hidden; authorization-specific commands // stay discoverable only through operator knowledge, not the public help/menu. // // Exposed (capitalised) so tests can assert on the string without spinning up // a bot context. func RenderHelp(reg *modules.Registry) string { if reg == nil { return "no commands registered\n\n" + supportFooter } byModule := make(map[string][]modules.Command, len(reg.Modules)) for _, c := range reg.PublicCommands() { byModule[ownerOf(reg, c.Name)] = append(byModule[ownerOf(reg, c.Name)], c) } var sections []string for _, mod := range reg.Modules { es := byModule[mod.Name] if len(es) == 0 { continue } var sb strings.Builder fmt.Fprintf(&sb, "%s", html.EscapeString(mod.Name)) for _, command := range es { fmt.Fprintf(&sb, "\n%s %s", html.EscapeString(command.InvocationSentence()), html.EscapeString(command.SummarySentence())) } sections = append(sections, sb.String()) } body := "no commands registered" if len(sections) > 0 { body = strings.Join(sections, "\n\n") } return body + "\n\n" + supportFooter } // ownerOf finds the module that registered the named command. Linear scan // (modules are few; commands per module are few). Returns "" if not found — // callers treat that as "skip". func ownerOf(reg *modules.Registry, cmdName string) string { for _, m := range reg.Modules { for _, c := range m.Commands { if c.Name == cmdName { return m.Name } } } return "" } // helpCommand returns /help — pure renderer over the registry. func helpCommand(reg *modules.Registry) modules.Command { return modules.Command{ Name: "help", Visibility: modules.VisibilityPublic, Description: "Show all available commands", Handler: func(ctx context.Context, b *bot.Bot, update *models.Update) error { if update.Message == nil { return nil } text := RenderHelp(reg) _, err := b.SendMessage(ctx, &bot.SendMessageParams{ ChatID: update.Message.Chat.ID, MessageThreadID: update.Message.MessageThreadID, Text: text, ParseMode: models.ParseModeHTML, LinkPreviewOptions: &models.LinkPreviewOptions{IsDisabled: bot.True()}, }) return err }, } }