From 95b7836beb0b35f604999adb79af3d161dec705d Mon Sep 17 00:00:00 2001 From: Syfaro Date: Sun, 3 Jan 2016 22:48:52 -0600 Subject: [PATCH 01/15] Remove deprecated methods. --- configs.go | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/configs.go b/configs.go index d7b146a..4150842 100644 --- a/configs.go +++ b/configs.go @@ -3,7 +3,6 @@ package tgbotapi import ( "encoding/json" "io" - "log" "net/url" "strconv" ) @@ -91,7 +90,6 @@ func (chat *BaseChat) values() (url.Values, error) { // BaseFile is a base type for all file config types. type BaseFile struct { BaseChat - FilePath string File interface{} FileID string UseExisting bool @@ -135,16 +133,7 @@ func (file BaseFile) params() (map[string]string, error) { // getFile returns the file. func (file BaseFile) getFile() interface{} { - var result interface{} - if file.FilePath == "" { - result = file.File - } else { - log.Println("FilePath is deprecated.") - log.Println("Please use BaseFile.File instead.") - result = file.FilePath - } - - return result + return file.File } // useExistingFile returns if the BaseFile has already been uploaded. From 82d5d362c39fa771d2999fc9c2975eed1116dc10 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Sun, 3 Jan 2016 22:49:58 -0600 Subject: [PATCH 02/15] Replace bad file type error with constant --- bot.go | 2 +- configs.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/bot.go b/bot.go index eb5a20e..eeb8127 100644 --- a/bot.go +++ b/bot.go @@ -146,7 +146,7 @@ func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldna ms.WriteReader(fieldname, f.Name, int64(len(data)), buf) default: - return APIResponse{}, errors.New("bad file type") + return APIResponse{}, errors.New(ErrBadFileType) } method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint) diff --git a/configs.go b/configs.go index 4150842..d1db4ac 100644 --- a/configs.go +++ b/configs.go @@ -39,6 +39,12 @@ const ( ModeMarkdown = "Markdown" ) +// Library errors +const ( + // ErrBadFileType happens when you pass an unknown type + ErrBadFileType = "bad file type" +) + // Chattable is any config type that can be sent. type Chattable interface { values() (url.Values, error) From e99a807c58f83ac3528b68db133ef4360c66e156 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Sun, 3 Jan 2016 22:50:29 -0600 Subject: [PATCH 03/15] Rename all errors to be prefixed with Err. --- bot.go | 2 +- configs.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bot.go b/bot.go index eeb8127..4a02e68 100644 --- a/bot.go +++ b/bot.go @@ -64,7 +64,7 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, defer resp.Body.Close() if resp.StatusCode == http.StatusForbidden { - return APIResponse{}, errors.New(APIForbidden) + return APIResponse{}, errors.New(ErrAPIForbidden) } bytes, err := ioutil.ReadAll(resp.Body) diff --git a/configs.go b/configs.go index d1db4ac..740e913 100644 --- a/configs.go +++ b/configs.go @@ -30,8 +30,8 @@ const ( // API errors const ( - // APIForbidden happens when a token is bad - APIForbidden = "forbidden" + // ErrAPIForbidden happens when a token is bad + ErrAPIForbidden = "forbidden" ) // Constant values for ParseMode in MessageConfig From bdfc7f89b1f6f8b385ef1fecd0ab0995a5dcf171 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Sun, 3 Jan 2016 23:05:36 -0600 Subject: [PATCH 04/15] Stop returning useless http.Handler from ListenForWebhook. --- README.md | 2 +- bot.go | 8 +++----- bot_test.go | 18 +----------------- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 0520f27..9ca6f6d 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ func main() { log.Fatal(err) } - updates, _ := bot.ListenForWebhook("/" + bot.Token) + updates := bot.ListenForWebhook("/" + bot.Token) go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil) for update := range updates { diff --git a/bot.go b/bot.go index 4a02e68..e924d4b 100644 --- a/bot.go +++ b/bot.go @@ -452,10 +452,10 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (<-chan Update, error) { } // ListenForWebhook registers a http handler for a webhook. -func (bot *BotAPI) ListenForWebhook(pattern string) (<-chan Update, http.Handler) { +func (bot *BotAPI) ListenForWebhook(pattern string) <-chan Update { updatesChan := make(chan Update, 100) - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { bytes, _ := ioutil.ReadAll(r.Body) var update Update @@ -464,9 +464,7 @@ func (bot *BotAPI) ListenForWebhook(pattern string) (<-chan Update, http.Handler updatesChan <- update }) - http.HandleFunc(pattern, handler) - - return updatesChan, handler + return updatesChan } // AnswerInlineQuery sends a response to an inline query. diff --git a/bot_test.go b/bot_test.go index e61c87c..0d22046 100644 --- a/bot_test.go +++ b/bot_test.go @@ -5,9 +5,7 @@ import ( "io/ioutil" "log" "net/http" - "net/http/httptest" "os" - "strings" "testing" ) @@ -351,20 +349,6 @@ func TestGetUserProfilePhotos(t *testing.T) { } } -func TestListenForWebhook(t *testing.T) { - bot, _ := getBot(t) - - _, handler := bot.ListenForWebhook("/") - - req, _ := http.NewRequest("GET", "", strings.NewReader("{}")) - w := httptest.NewRecorder() - - handler.ServeHTTP(w, req) - if w.Code != http.StatusOK { - t.Errorf("Home page didn't return %v", http.StatusOK) - } -} - func TestSetWebhookWithCert(t *testing.T) { bot, _ := getBot(t) @@ -445,7 +429,7 @@ func ExampleNewWebhook() { log.Fatal(err) } - updates, _ := bot.ListenForWebhook("/" + bot.Token) + updates := bot.ListenForWebhook("/" + bot.Token) go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil) for update := range updates { From c805a455209910b6004c500e40d9a0ed241cfda2 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 4 Jan 2016 11:19:13 -0600 Subject: [PATCH 05/15] Remove other deprecated method. --- types.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/types.go b/types.go index 9c74082..90e8b61 100644 --- a/types.go +++ b/types.go @@ -123,15 +123,6 @@ func (m *Message) Time() time.Time { return time.Unix(int64(m.Date), 0) } -// IsGroup returns if the message was sent to a group. -// -// Deprecated in favor of Chat.IsGroup. -func (m *Message) IsGroup() bool { - log.Println("Message.IsGroup is deprecated.") - log.Println("Please use Chat.IsGroup instead.") - return m.Chat.IsGroup() -} - // IsCommand returns true if message starts with '/'. func (m *Message) IsCommand() bool { return m.Text != "" && m.Text[0] == '/' From 4a1b9aa93832c3db355b21390e8dea0326256b87 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 4 Jan 2016 11:21:20 -0600 Subject: [PATCH 06/15] Remove log import. --- types.go | 1 - 1 file changed, 1 deletion(-) diff --git a/types.go b/types.go index 90e8b61..8e49ad4 100644 --- a/types.go +++ b/types.go @@ -3,7 +3,6 @@ package tgbotapi import ( "encoding/json" "fmt" - "log" "strings" "time" ) From 93225440a7eab2bb91e2ee94573cabf7e2001769 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 4 Jan 2016 11:45:46 -0600 Subject: [PATCH 07/15] Don't return slash in Command, strip bot name if needed. --- types.go | 10 +++++++++- types_test.go | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/types.go b/types.go index 8e49ad4..a8a9c7e 100644 --- a/types.go +++ b/types.go @@ -129,12 +129,20 @@ func (m *Message) IsCommand() bool { // 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. func (m *Message) Command() string { if !m.IsCommand() { return "" } - return strings.SplitN(m.Text, " ", 2)[0] + command := strings.SplitN(m.Text, " ", 2)[0][1:] + + if i := strings.Index(command, "@"); i != -1 { + command = command[:i] + } + + return command } // CommandArguments checks if the message was a command and if it was, diff --git a/types_test.go b/types_test.go index 8e10e22..6588251 100644 --- a/types_test.go +++ b/types_test.go @@ -58,7 +58,7 @@ func TestIsCommandWithEmptyText(t *testing.T) { func TestCommandWithCommand(t *testing.T) { message := tgbotapi.Message{Text: "/command"} - if message.Command() != "/command" { + if message.Command() != "command" { t.Fail() } } @@ -79,6 +79,14 @@ func TestCommandWithNonCommand(t *testing.T) { } } +func TestCommandWithBotName(t *testing.T) { + message := tgbotapi.Message{Text: "/command@testbot"} + + if message.Command() != "command" { + t.Fail() + } +} + func TestMessageCommandArgumentsWithArguments(t *testing.T) { message := tgbotapi.Message{Text: "/command with arguments"} if message.CommandArguments() != "with arguments" { From f8593d08585c80ad7bada0b8aacd90447612f836 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 4 Jan 2016 14:55:04 -0600 Subject: [PATCH 08/15] Update README for v2 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9ca6f6d..2d0fce8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ something with plugins and command handlers without having to design all that yourself. Use `github.com/go-telegram-bot-api/telegram-bot-api` for the latest -version, or use `gopkg.in/telegram-bot-api.v1` for the stable build. +version, or use `gopkg.in/telegram-bot-api.v2` for the stable build. ## Example @@ -29,7 +29,7 @@ package main import ( "log" - "gopkg.in/telegram-bot-api.v1" + "gopkg.in/telegram-bot-api.v2" ) func main() { @@ -65,7 +65,7 @@ you may use a slightly different method. package main import ( - "gopkg.in/telegram-bot-api.v1" + "gopkg.in/telegram-bot-api.v2" "log" "net/http" ) From 8a8b8a063d04621e42ee092a054fd37acc631a36 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Sun, 17 Jan 2016 23:34:12 -0600 Subject: [PATCH 09/15] Fix broken inline queries. --- bot.go | 2 ++ configs.go | 10 +++++----- types.go | 23 +++++++++++------------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/bot.go b/bot.go index e924d4b..911756c 100644 --- a/bot.go +++ b/bot.go @@ -483,5 +483,7 @@ func (bot *BotAPI) AnswerInlineQuery(config InlineConfig) (APIResponse, error) { } v.Add("results", string(data)) + bot.debugLog("answerInlineQuery", v, nil) + return bot.MakeRequest("answerInlineQuery", v) } diff --git a/configs.go b/configs.go index 740e913..da96905 100644 --- a/configs.go +++ b/configs.go @@ -510,9 +510,9 @@ type FileReader struct { // InlineConfig contains information on making an InlineQuery response. type InlineConfig struct { - InlineQueryID string `json:"inline_query_id"` - Results []InlineQueryResult `json:"results"` - CacheTime int `json:"cache_time"` - IsPersonal bool `json:"is_personal"` - NextOffset string `json:"next_offset"` + InlineQueryID string `json:"inline_query_id"` + Results []interface{} `json:"results"` + CacheTime int `json:"cache_time"` + IsPersonal bool `json:"is_personal"` + NextOffset string `json:"next_offset"` } diff --git a/types.go b/types.go index a8a9c7e..8a738f4 100644 --- a/types.go +++ b/types.go @@ -281,18 +281,13 @@ type InlineQuery struct { Offset string `json:"offset"` } -// InlineQueryResult is the base type that all InlineQuery Results have. -type InlineQueryResult struct { - Type string `json:"type"` // required - ID string `json:"id"` // required -} - // InlineQueryResultArticle is an inline query response article. type InlineQueryResultArticle struct { - InlineQueryResult + Type string `json:"type"` // required + ID string `json:"id"` // required Title string `json:"title"` // required MessageText string `json:"message_text"` // required - ParseMode string `json:"parse_mode"` // required + ParseMode string `json:"parse_mode"` DisableWebPagePreview bool `json:"disable_web_page_preview"` URL string `json:"url"` HideURL bool `json:"hide_url"` @@ -304,7 +299,8 @@ type InlineQueryResultArticle struct { // InlineQueryResultPhoto is an inline query response photo. type InlineQueryResultPhoto struct { - InlineQueryResult + Type string `json:"type"` // required + ID string `json:"id"` // required URL string `json:"photo_url"` // required MimeType string `json:"mime_type"` Width int `json:"photo_width"` @@ -320,7 +316,8 @@ type InlineQueryResultPhoto struct { // InlineQueryResultGIF is an inline query response GIF. type InlineQueryResultGIF struct { - InlineQueryResult + Type string `json:"type"` // required + ID string `json:"id"` // required URL string `json:"gif_url"` // required Width int `json:"gif_width"` Height int `json:"gif_height"` @@ -334,7 +331,8 @@ type InlineQueryResultGIF struct { // InlineQueryResultMPEG4GIF is an inline query response MPEG4 GIF. type InlineQueryResultMPEG4GIF struct { - InlineQueryResult + Type string `json:"type"` // required + ID string `json:"id"` // required URL string `json:"mpeg4_url"` // required Width int `json:"mpeg4_width"` Height int `json:"mpeg4_height"` @@ -348,7 +346,8 @@ type InlineQueryResultMPEG4GIF struct { // InlineQueryResultVideo is an inline query response video. type InlineQueryResultVideo struct { - InlineQueryResult + Type string `json:"type"` // required + ID string `json:"id"` // required URL string `json:"video_url"` // required MimeType string `json:"mime_type"` // required MessageText string `json:"message_text"` // required From 788f7517cd6555257ac602b3d002bfebfa2ee70a Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 18 Jan 2016 00:06:48 -0600 Subject: [PATCH 10/15] Add helpers for creating inline query results. --- helpers.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/helpers.go b/helpers.go index bb9dc1a..6dcc91a 100644 --- a/helpers.go +++ b/helpers.go @@ -281,3 +281,49 @@ func NewWebhookWithCert(link string, file interface{}) WebhookConfig { Certificate: file, } } + +// NewInlineQueryResultArticle creates a new inline query article. +func NewInlineQueryResultArticle(id, title, messageText string) InlineQueryResultArticle { + return InlineQueryResultArticle{ + Type: "article", + ID: id, + Title: title, + MessageText: messageText, + } +} + +// NewInlineQueryResultGIF creates a new inline query GIF. +func NewInlineQueryResultGIF(id, url string) InlineQueryResultGIF { + return InlineQueryResultGIF{ + Type: "gif", + ID: id, + URL: url, + } +} + +// NewInlineQueryResultMPEG4GIF creates a new inline query MPEG4 GIF. +func NewInlineQueryResultMPEG4GIF(id, url string) InlineQueryResultMPEG4GIF { + return InlineQueryResultMPEG4GIF{ + Type: "mpeg4_gif", + ID: id, + URL: url, + } +} + +// NewInlineQueryResultPhoto creates a new inline query photo. +func NewInlineQueryResultPhoto(id, url string) InlineQueryResultPhoto { + return InlineQueryResultPhoto{ + Type: "photo", + ID: id, + URL: url, + } +} + +// NewInlineQueryResultVideo creates a new inline query video. +func NewInlineQueryResultVideo(id, url string) InlineQueryResultVideo { + return InlineQueryResultVideo{ + Type: "video", + ID: id, + URL: url, + } +} From 4d3516d035c0eb5db8b78a59e253e2865843fa5a Mon Sep 17 00:00:00 2001 From: Syfaro Date: Wed, 20 Jan 2016 15:30:50 -0600 Subject: [PATCH 11/15] Add ModeHTML for HTML message formatting. --- configs.go | 1 + 1 file changed, 1 insertion(+) diff --git a/configs.go b/configs.go index da96905..19f4b18 100644 --- a/configs.go +++ b/configs.go @@ -37,6 +37,7 @@ const ( // Constant values for ParseMode in MessageConfig const ( ModeMarkdown = "Markdown" + ModeHTML = "HTML" ) // Library errors From 9db8f49c488d40344508f2eaab3a5dd17a1cac3b Mon Sep 17 00:00:00 2001 From: Syfaro Date: Thu, 25 Feb 2016 07:47:20 -0600 Subject: [PATCH 12/15] Add support for messages with disabled notifications. --- configs.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/configs.go b/configs.go index 19f4b18..1839a5d 100644 --- a/configs.go +++ b/configs.go @@ -63,10 +63,11 @@ type Fileable interface { // BaseChat is base type for all chat config types. type BaseChat struct { - ChatID int // required - ChannelUsername string - ReplyToMessageID int - ReplyMarkup interface{} + ChatID int // required + ChannelUsername string + ReplyToMessageID int + ReplyMarkup interface{} + DisableNotification bool } // values returns url.Values representation of BaseChat @@ -91,6 +92,8 @@ func (chat *BaseChat) values() (url.Values, error) { v.Add("reply_markup", string(data)) } + v.Add("disable_notification", strconv.FormatBool(chat.DisableNotification)) + return v, nil } @@ -135,6 +138,8 @@ func (file BaseFile) params() (map[string]string, error) { params["file_size"] = strconv.Itoa(file.FileSize) } + params["disable_notification"] = strconv.FormatBool(file.DisableNotification) + return params, nil } From 445a4ef1390dd796b005d5be7d93fe881c218cf6 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 26 Feb 2016 08:58:14 -0600 Subject: [PATCH 13/15] Fix the From field in the Inline Query. --- types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types.go b/types.go index 8a738f4..708aa99 100644 --- a/types.go +++ b/types.go @@ -276,7 +276,7 @@ type ForceReply struct { // InlineQuery is a Query from Telegram for an inline request. type InlineQuery struct { ID string `json:"id"` - From User `json:"user"` + From User `json:"from"` Query string `json:"query"` Offset string `json:"offset"` } From 6bc2e5ec10fb0fac85b35267664810f821a4f841 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Sun, 28 Feb 2016 10:13:44 -0600 Subject: [PATCH 14/15] Remove a stray word from UserProfilePhotos comment. --- types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types.go b/types.go index 708aa99..ef16c10 100644 --- a/types.go +++ b/types.go @@ -232,7 +232,7 @@ type Location struct { Latitude float32 `json:"latitude"` } -// UserProfilePhotos contains information a set of user profile photos. +// UserProfilePhotos contains a set of user profile photos. type UserProfilePhotos struct { TotalCount int `json:"total_count"` Photos []PhotoSize `json:"photos"` From 6faae88cbd1dad3d6c06c2a290f50fa38894f90e Mon Sep 17 00:00:00 2001 From: Syfaro Date: Sun, 28 Feb 2016 10:14:08 -0600 Subject: [PATCH 15/15] Add example for responding to inline queries. --- bot_test.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/bot_test.go b/bot_test.go index 0d22046..215e08b 100644 --- a/bot_test.go +++ b/bot_test.go @@ -436,3 +436,37 @@ func ExampleNewWebhook() { log.Printf("%+v\n", update) } } + +func ExampleAnswerInlineQuery() { + bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") // create new bot + if err != nil { + log.Panic(err) + } + + log.Printf("Authorized on account %s", bot.Self.UserName) + + u := tgbotapi.NewUpdate(0) + u.Timeout = 60 + + updates, err := bot.GetUpdatesChan(u) + + for update := range updates { + if update.InlineQuery.Query == "" { // if no inline query, ignore it + continue + } + + article := tgbotapi.NewInlineQueryResultArticle(update.InlineQuery.ID, "Echo", update.InlineQuery.Query) + article.Description = update.InlineQuery.Query + + inlineConf := tgbotapi.InlineConfig{ + InlineQueryID: update.InlineQuery.ID, + IsPersonal: true, + CacheTime: 0, + Results: []interface{}{article}, + } + + if _, err := bot.AnswerInlineQuery(inlineConf); err != nil { + log.Println(err) + } + } +}