fix(commands): remove examples from command discovery

This commit is contained in:
tiennm99 committed 2026-07-21 13:52:53 +07:00
1 parent b5c7427520
commit a211b7f8b7
22 files changed
+47 -154

No files matched your search

-4
View File
@@ -16,7 +16,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Show current crypto price in USD",
Parameters: "<coin>",
Example: "/coin_price BTC",
Handler: s.handlePrice,
},
{
@@ -24,7 +23,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Top up USD to your coin account",
Parameters: "<usd_amount>",
Example: "/coin_topup 1000",
Handler: s.handleTopup,
},
{
@@ -32,7 +30,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Spend a USD amount to buy coin",
Parameters: "<coin> <usd_to_spend>",
Example: "/coin_buy BTC 10",
Handler: s.handleBuy,
},
{
@@ -40,7 +37,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Sell enough coin to receive a USD amount",
Parameters: "<coin> <usd_to_receive>",
Example: "/coin_sell BTC 10",
Handler: s.handleSell,
},
{
-9
View File
@@ -17,11 +17,6 @@ func (c Command) InvocationSentence() string {
return withTerminalPunctuation(c.Invocation())
}
// ExampleInvocation returns the explicitly registered copyable example.
func (c Command) ExampleInvocation() string {
return strings.TrimSpace(c.Example)
}
// SummarySentence normalizes a command summary to a sentence without
// duplicating terminal punctuation supplied by the registration.
func (c Command) SummarySentence() string {
@@ -38,10 +33,6 @@ func (c Command) TelegramMenuDescription() string {
sb.WriteByte(' ')
}
sb.WriteString(c.SummarySentence())
if example := c.ExampleInvocation(); example != "" {
sb.WriteString(" Eg: ")
sb.WriteString(example)
}
return sb.String()
}
+4 -13
View File
@@ -7,26 +7,22 @@ func TestCommandPresentation(t *testing.T) {
name string
command Command
invocation string
example string
menu string
}{
{
name: "parameters and explicit example",
name: "parameters",
command: Command{
Name: "stock_buy",
Parameters: "<quantity> <ticker>",
Description: "Buy VN stock at market price",
Example: "/stock_buy 100 TCB",
},
invocation: "/stock_buy <quantity> <ticker>",
example: "/stock_buy 100 TCB",
menu: "<quantity> <ticker>. Buy VN stock at market price. Eg: /stock_buy 100 TCB",
menu: "<quantity> <ticker>. Buy VN stock at market price.",
},
{
name: "no parameters omits example",
name: "no parameters",
command: Command{Name: "ping", Description: "Health check!"},
invocation: "/ping",
example: "",
menu: "Health check!",
},
{
@@ -35,11 +31,9 @@ func TestCommandPresentation(t *testing.T) {
Name: "random",
Parameters: "<options(comma-separated)>",
Description: "Pick one option",
Example: "/random pizza, sushi",
},
invocation: "/random <options(comma-separated)>",
example: "/random pizza, sushi",
menu: "<options(comma-separated)>. Pick one option. Eg: /random pizza, sushi",
menu: "<options(comma-separated)>. Pick one option.",
},
}
@@ -48,9 +42,6 @@ func TestCommandPresentation(t *testing.T) {
if got := tc.command.Invocation(); got != tc.invocation {
t.Errorf("Invocation() = %q, want %q", got, tc.invocation)
}
if got := tc.command.ExampleInvocation(); got != tc.example {
t.Errorf("ExampleInvocation() = %q, want %q", got, tc.example)
}
if got := tc.command.TelegramMenuDescription(); got != tc.menu {
t.Errorf("TelegramMenuDescription() = %q, want %q", got, tc.menu)
}
-3
View File
@@ -21,7 +21,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Top up VND to your gold account",
Parameters: "<vnd_amount>",
Example: "/gold_topup 5000000",
Handler: s.handleTopup,
},
{
@@ -29,7 +28,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Buy gold at SJC sell price",
Parameters: "<luong>",
Example: "/gold_buy 1",
Handler: s.handleBuy,
},
{
@@ -37,7 +35,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Sell gold at SJC buy price",
Parameters: "<luong>",
Example: "/gold_sell 0.5",
Handler: s.handleSell,
},
{
-1
View File
@@ -27,7 +27,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "LoL matches for a date (dd, dd-mm, dd/mm, ddmm, or full date; default today)",
Parameters: "[date]",
Example: "/lol 21-07-2026",
Handler: s.handleSchedule,
},
{
-1
View File
@@ -21,7 +21,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Classic loldle — guess the current champion",
Parameters: "[champion]",
Example: "/loldle Ahri",
Handler: s.handleLoldle,
},
{
-3
View File
@@ -129,17 +129,14 @@ func senderMention(u *models.User) string {
func disclaimerCommand(name, description, defaultTarget string, allowCustomTarget bool) modules.Command {
parameters := ""
example := ""
if allowCustomTarget {
parameters = "[target...]"
example = "/" + name + " FBI"
}
return modules.Command{
Name: name,
Visibility: modules.VisibilityPublic,
Description: description,
Parameters: parameters,
Example: example,
Handler: func(ctx context.Context, b *bot.Bot, update *models.Update) error {
if update.Message == nil {
return nil
-1
View File
@@ -31,7 +31,6 @@ func randomCommand() modules.Command {
Visibility: modules.VisibilityPublic,
Description: "Pick one random comma-separated option",
Parameters: "<options(comma-separated)>",
Example: "/random pizza, sushi, pho",
Handler: func(ctx context.Context, b *bot.Bot, update *models.Update) error {
if update.Message == nil {
return nil
@@ -25,7 +25,6 @@ func wheelOfNamesCommand() modules.Command {
Visibility: modules.VisibilityPublic,
Description: "Pick one comma-separated option with wheel GIF when configured",
Parameters: "<options(comma-separated)>",
Example: "/wheelofnames pizza, sushi, pho",
Handler: func(ctx context.Context, b *bot.Bot, update *models.Update) error {
if update.Message == nil {
return nil
-1
View File
@@ -40,7 +40,6 @@ type Command struct {
Visibility Visibility // public/protected/private
Description string // concise summary shown in command discovery (required, non-empty)
Parameters string // optional syntax after the command, e.g. "<quantity> <ticker>"
Example string // full invocation required with public Parameters; omitted without them
Handler CommandHandler // required
}
-1
View File
@@ -33,7 +33,6 @@ func statsCommand(c *counter) modules.Command {
Visibility: modules.VisibilityPublic,
Description: "Show command usage statistics",
Parameters: "[users | user <username> | cmd <command_name>]",
Example: "/stats user alice",
Handler: func(ctx context.Context, b *bot.Bot, update *models.Update) error {
if update.Message == nil {
return nil
-7
View File
@@ -15,7 +15,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Show current VN stock price",
Parameters: "<ticker>",
Example: "/stock_price TCB",
Handler: s.handlePrice,
},
{
@@ -23,7 +22,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Top up VND to your stock account",
Parameters: "<vnd_amount>",
Example: "/stock_topup 5000000",
Handler: s.handleTopup,
},
{
@@ -31,7 +29,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Buy VN stock at market price",
Parameters: "<quantity> <ticker>",
Example: "/stock_buy 100 TCB",
Handler: s.handleBuy,
},
{
@@ -39,7 +36,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Sell VN stock back to VND",
Parameters: "<quantity> <ticker>",
Example: "/stock_sell 100 TCB",
Handler: s.handleSell,
},
{
@@ -47,7 +43,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Record cash dividend",
Parameters: "<vnd_per_share> <ticker>",
Example: "/stock_cash_dividend 1500 TCB",
Handler: s.handleCashDividend,
},
{
@@ -55,7 +50,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Record share dividend",
Parameters: "<ratio(owned:new)> <ticker>",
Example: "/stock_share_dividend 100:10 TCB",
Handler: s.handleShareDividend,
},
{
@@ -63,7 +57,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Record cash and share dividend",
Parameters: "<vnd_per_share> <ratio(owned:new)> <ticker>",
Example: "/stock_dividend 1500 100:10 TCB",
Handler: s.handleDividend,
},
{
-3
View File
@@ -50,9 +50,6 @@ func RenderHelp(reg *modules.Registry) string {
fmt.Fprintf(&sb, "\n%s %s",
html.EscapeString(command.InvocationSentence()),
html.EscapeString(command.SummarySentence()))
if example := command.ExampleInvocation(); example != "" {
fmt.Fprintf(&sb, " Eg: <code>%s</code>", html.EscapeString(example))
}
}
sections = append(sections, sb.String())
}
+3 -10
View File
@@ -74,13 +74,12 @@ func TestRenderHelp_GroupsByModuleAndSkipsNonPublic(t *testing.T) {
}
}
func TestRenderHelp_ShowsParametersAndCopyableExample(t *testing.T) {
func TestRenderHelp_ShowsParametersWithoutExample(t *testing.T) {
command := modules.Command{
Name: "buy",
Visibility: modules.VisibilityPublic,
Description: "Buy & hold",
Parameters: "<quantity> <ticker>",
Example: "/buy 100 TCB & hold",
Handler: helpTestNoop,
}
reg, err := modules.Build(
@@ -97,14 +96,8 @@ func TestRenderHelp_ShowsParametersAndCopyableExample(t *testing.T) {
if !strings.Contains(out, "/buy &lt;quantity&gt; &lt;ticker&gt;. Buy &amp; hold.") {
t.Fatalf("help missing formatted invocation and summary:\n%s", out)
}
if !strings.Contains(out, "<code>/buy 100 TCB &amp; hold</code>") {
t.Fatalf("help missing copyable example:\n%s", out)
}
if !strings.Contains(out, "Buy &amp; hold. Eg: <code>/buy 100 TCB &amp; hold</code>") {
t.Fatalf("help missing short example label:\n%s", out)
}
if strings.Contains(out, "<pre>") {
t.Fatalf("help example should use inline code, not a pre block:\n%s", out)
if strings.Contains(out, "Eg:") || strings.Contains(out, "<code>") || strings.Contains(out, "<pre>") {
t.Fatalf("help should not render examples:\n%s", out)
}
}
-19
View File
@@ -29,25 +29,6 @@ func validateCommand(c Command) error {
if strings.ContainsAny(c.Parameters, "\r\n") {
return fmt.Errorf("command %q: parameters must be single-line", c.Name)
}
if strings.ContainsAny(c.Example, "\r\n") {
return fmt.Errorf("command %q: example must be single-line", c.Name)
}
parameters := strings.TrimSpace(c.Parameters)
example := strings.TrimSpace(c.Example)
if c.Visibility == VisibilityPublic {
if parameters == "" && example != "" {
return fmt.Errorf("command %q: example requires parameters", c.Name)
}
if parameters != "" && example == "" {
return fmt.Errorf("command %q: example is required when parameters are present", c.Name)
}
}
if example != "" {
prefix := "/" + c.Name
if example != prefix && !strings.HasPrefix(example, prefix+" ") {
return fmt.Errorf("command %q: example must invoke %s", c.Name, prefix)
}
}
if c.Visibility == VisibilityPublic && utf8.RuneCountInString(c.TelegramMenuDescription()) > telegramCommandDescriptionMaxRunes {
return fmt.Errorf("command %q: Telegram menu description exceeds %d characters", c.Name, telegramCommandDescriptionMaxRunes)
}
+1 -18
View File
@@ -47,22 +47,6 @@ func TestValidateCommand_RejectsInvalidPresentationMetadata(t *testing.T) {
name: "multiline parameters",
command: Command{Name: "ok", Visibility: VisibilityPublic, Description: "d", Parameters: "<a>\n<b>", Handler: okHandler},
},
{
name: "multiline example",
command: Command{Name: "ok", Visibility: VisibilityPublic, Description: "d", Example: "/ok one\n/ok two", Handler: okHandler},
},
{
name: "example invokes another command",
command: Command{Name: "ok", Visibility: VisibilityPublic, Description: "d", Example: "/other", Handler: okHandler},
},
{
name: "example without parameters",
command: Command{Name: "ok", Visibility: VisibilityPublic, Description: "d", Example: "/ok", Handler: okHandler},
},
{
name: "parameters without example",
command: Command{Name: "ok", Visibility: VisibilityPublic, Description: "d", Parameters: "<value>", Handler: okHandler},
},
{
name: "native description too long",
command: Command{Name: "ok", Visibility: VisibilityPublic, Description: strings.Repeat("x", 257), Handler: okHandler},
@@ -86,13 +70,12 @@ func TestValidateCommand_AcceptsLegalNames(t *testing.T) {
}
}
func TestValidateCommand_AcceptsMatchedParametersAndExample(t *testing.T) {
func TestValidateCommand_AcceptsParameters(t *testing.T) {
command := Command{
Name: "ok",
Visibility: VisibilityPublic,
Description: "Do it",
Parameters: "<value>",
Example: "/ok demo",
Handler: okHandler,
}
if err := validateCommand(command); err != nil {
-1
View File
@@ -23,7 +23,6 @@ func New(deps modules.Deps) modules.Module {
Visibility: modules.VisibilityPublic,
Description: "Classic wordle — guess the 5-letter word",
Parameters: "[word]",
Example: "/wordle apple",
Handler: s.handleWordle,
},
{