summaryrefslogtreecommitdiffstats
path: root/internal/bot/command.go
blob: 9e130602277a07b1a83408cee75f6af8d5680e34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package bot

import "strings"

// Command is a parsed "/name@target argument" line.
type Command struct {
	// Name is the command without the leading slash, lowercased.
	Name string

	// Target is the bot username the command was addressed to, without the
	// "@", or "" when the user did not address anyone.
	Target string

	// Argument is everything after the command, trimmed.
	Argument string
}

// ParseCommand splits a message that starts with a slash.
//
// The "@username" suffix is not optional to handle. Telegram appends it
// automatically whenever more than one bot is in a chat, so in exactly the busy
// groups where the bot is most visible, every command arrives as
// "/cep@thebotname 01001000". A bot that only matches the bare form looks broken
// there.
//
// ok is false when the text is not a command at all.
func ParseCommand(text string) (Command, bool) {
	trimmed := strings.TrimSpace(text)
	if !strings.HasPrefix(trimmed, "/") || len(trimmed) == 1 {
		return Command{}, false
	}

	head, argument, _ := strings.Cut(trimmed[1:], " ")
	name, target, _ := strings.Cut(head, "@")
	if name == "" {
		return Command{}, false
	}

	return Command{
		Name:     strings.ToLower(name),
		Target:   target,
		Argument: strings.TrimSpace(argument),
	}, true
}

// AddressedTo reports whether the command is for the bot with this username.
//
// An unaddressed command counts as addressed: in a private chat nobody types
// the suffix, and in a group with a single bot Telegram does not add it either.
// A command addressed to some other bot is not ours, and answering it would put
// two replies under one message.
func (c Command) AddressedTo(username string) bool {
	return c.Target == "" || strings.EqualFold(c.Target, username)
}