From 5f5f94047c7a2f67e0d19bd5f1012321a0579e8a Mon Sep 17 00:00:00 2001 From: Henner Date: Sat, 7 Oct 2017 16:51:53 +0200 Subject: [PATCH 1/3] Rewrite message command methods to use entities Since the Telegram-API considers "/foo-bar" to be a command entity "/foo", we had choose a behaviour for the CommandArguments() method. The specification (https://core.telegram.org/bots#commands) mandates a space after the command name, so we decided to skip the first character following the command entity. However, Telegram clients render "/foo-bar" as if "/foo" was the command and "-bar" additional text, so this alternative should be remembered. --- types.go | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/types.go b/types.go index 95231e4..91875bb 100644 --- a/types.go +++ b/types.go @@ -173,21 +173,23 @@ func (m *Message) Time() time.Time { return time.Unix(int64(m.Date), 0) } -// IsCommand returns true if message starts with '/'. +// IsCommand returns true if message starts with a "bot_command" entity. func (m *Message) IsCommand() bool { - return m.Text != "" && m.Text[0] == '/' + if m.Entities == nil || len(*m.Entities) == 0 { + return false + } + + entity := (*m.Entities)[0] + return entity.Offset == 0 && entity.Type == "bot_command" } // Command checks if the message was a command and if it was, returns the // command. If the Message was not a command, it returns an empty string. // -// If the command contains the at bot syntax, it removes the bot name. +// If the command contains the at name syntax, it is removed. Use +// CommandWithAt() if you do not want that. func (m *Message) Command() string { - if !m.IsCommand() { - return "" - } - - command := strings.SplitN(m.Text, " ", 2)[0][1:] + command := m.CommandWithAt() if i := strings.Index(command, "@"); i != -1 { command = command[:i] @@ -196,20 +198,42 @@ func (m *Message) Command() string { return command } +// CommandWithAt checks if the message was a command and if it was, returns the +// command. If the Message was not a command, it returns an empty string. +// +// If the command contains the at name syntax, it is not removed. Use Command() +// if you want that. +func (m *Message) CommandWithAt() string { + if !m.IsCommand() { + return "" + } + + // IsCommand() checks that the message begins with a bot_command entity + entity := (*m.Entities)[0] + return m.Text[1:entity.Length] +} + // CommandArguments checks if the message was a command and if it was, // returns all text after the command name. If the Message was not a // command, it returns an empty string. +// +// Note: The first character after the command name is omitted: +// - "/foo bar baz" yields "bar baz", not " bar baz" +// - "/foo-bar baz" yields "bar baz", too +// Even though the latter is not a command conforming to the spec, the API +// marks "/foo" as command entity. func (m *Message) CommandArguments() string { if !m.IsCommand() { return "" } - split := strings.SplitN(m.Text, " ", 2) - if len(split) != 2 { - return "" + // IsCommand() checks that the message begins with a bot_command entity + entity := (*m.Entities)[0] + if len(m.Text) == entity.Length { + return "" // The command makes up the whole message + } else { + return m.Text[entity.Length+1:] } - - return split[1] } // MessageEntity contains information about data in a Message. From c697a95948b08fd61419986d493601d14f3b3744 Mon Sep 17 00:00:00 2001 From: Henner Date: Sat, 7 Oct 2017 17:53:50 +0200 Subject: [PATCH 2/3] Fix failing tests --- types_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/types_test.go b/types_test.go index d2c24d4..ed8d625 100644 --- a/types_test.go +++ b/types_test.go @@ -34,6 +34,7 @@ func TestMessageTime(t *testing.T) { func TestMessageIsCommandWithCommand(t *testing.T) { message := tgbotapi.Message{Text: "/command"} + message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if message.IsCommand() != true { t.Fail() @@ -58,6 +59,7 @@ func TestIsCommandWithEmptyText(t *testing.T) { func TestCommandWithCommand(t *testing.T) { message := tgbotapi.Message{Text: "/command"} + message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if message.Command() != "command" { t.Fail() @@ -82,6 +84,7 @@ func TestCommandWithNonCommand(t *testing.T) { func TestCommandWithBotName(t *testing.T) { message := tgbotapi.Message{Text: "/command@testbot"} + message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} if message.Command() != "command" { t.Fail() @@ -90,6 +93,7 @@ func TestCommandWithBotName(t *testing.T) { func TestMessageCommandArgumentsWithArguments(t *testing.T) { message := tgbotapi.Message{Text: "/command with arguments"} + message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if message.CommandArguments() != "with arguments" { t.Fail() } From b8425b053e0ac70e917076ef036661da382bd7c3 Mon Sep 17 00:00:00 2001 From: Henner Date: Sat, 7 Oct 2017 18:06:20 +0200 Subject: [PATCH 3/3] Add missing tests --- types_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/types_test.go b/types_test.go index ed8d625..9ef03b1 100644 --- a/types_test.go +++ b/types_test.go @@ -91,6 +91,15 @@ func TestCommandWithBotName(t *testing.T) { } } +func TestCommandWithAtWithBotName(t *testing.T) { + message := tgbotapi.Message{Text: "/command@testbot"} + message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} + + if message.CommandWithAt() != "command@testbot" { + t.Fail() + } +} + func TestMessageCommandArgumentsWithArguments(t *testing.T) { message := tgbotapi.Message{Text: "/command with arguments"} message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} @@ -99,6 +108,14 @@ func TestMessageCommandArgumentsWithArguments(t *testing.T) { } } +func TestMessageCommandArgumentsWithMalformedArguments(t *testing.T) { + message := tgbotapi.Message{Text: "/command-without argument space"} + message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} + if message.CommandArguments() != "without argument space" { + t.Fail() + } +} + func TestMessageCommandArgumentsWithoutArguments(t *testing.T) { message := tgbotapi.Message{Text: "/command"} if message.CommandArguments() != "" {