Merge master into develop.

bot-api-6.1
Syfaro 2018-03-26 12:22:16 -05:00
commit 6a6de7e674
6 changed files with 170 additions and 44 deletions

View File

@ -91,6 +91,16 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
info, err := bot.GetWebhookInfo()
if err != nil {
log.Fatal(err)
}
if info.LastErrorDate != 0 {
log.Printf("failed to set webhook: %s", info.LastErrorMessage)
}
updates := bot.ListenForWebhook("/" + bot.Token) updates := bot.ListenForWebhook("/" + bot.Token)
go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil) go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil)

87
bot.go
View File

@ -7,6 +7,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
@ -71,28 +72,56 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse,
} }
defer resp.Body.Close() defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body) var apiResp APIResponse
bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp)
if err != nil { if err != nil {
return APIResponse{}, err return apiResp, err
} }
if bot.Debug { if bot.Debug {
log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes)) log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes))
} }
var apiResp APIResponse if !apiResp.Ok {
err = json.Unmarshal(bytes, &apiResp) var parameters ResponseParameters
if err != nil {
return APIResponse{}, err if apiResp.Parameters != nil {
parameters = *apiResp.Parameters
} }
if !apiResp.Ok { return apiResp, Error{
return apiResp, errors.New(apiResp.Description) Message: apiResp.Description,
ResponseParameters: parameters,
}
} }
return apiResp, nil return apiResp, nil
} }
// decodeAPIResponse decode response and return slice of bytes if debug enabled.
// If debug disabled, just decode http.Response.Body stream to APIResponse struct
// for efficient memory usage
func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse) (_ []byte, err error) {
if !bot.Debug {
dec := json.NewDecoder(responseBody)
err = dec.Decode(resp)
return
}
// if debug, read reponse body
data, err := ioutil.ReadAll(responseBody)
if err != nil {
return
}
err = json.Unmarshal(data, resp)
if err != nil {
return
}
return data, nil
}
// UploadFile makes a request to the API with a file. // UploadFile makes a request to the API with a file.
// //
// Requires the parameter to hold the file not be in the params. // Requires the parameter to hold the file not be in the params.
@ -543,3 +572,45 @@ func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error)
return stickers, err return stickers, err
} }
// SetChatTitle change title of chat.
func (bot *BotAPI) SetChatTitle(config SetChatTitleConfig) (APIResponse, error) {
v, err := config.values()
if err != nil {
return APIResponse{}, err
}
return bot.MakeRequest(config.method(), v)
}
// SetChatDescription change description of chat.
func (bot *BotAPI) SetChatDescription(config SetChatDescriptionConfig) (APIResponse, error) {
v, err := config.values()
if err != nil {
return APIResponse{}, err
}
return bot.MakeRequest(config.method(), v)
}
// SetChatPhoto change photo of chat.
func (bot *BotAPI) SetChatPhoto(config SetChatPhotoConfig) (APIResponse, error) {
params, err := config.params()
if err != nil {
return APIResponse{}, err
}
file := config.getFile()
return bot.UploadFile(config.method(), params, config.name(), file)
}
// DeleteChatPhoto delete photo of chat.
func (bot *BotAPI) DeleteChatPhoto(config DeleteChatPhotoConfig) (APIResponse, error) {
v, err := config.values()
if err != nil {
return APIResponse{}, err
}
return bot.MakeRequest(config.method(), v)
}

View File

@ -476,6 +476,16 @@ func TestSetWebhookWithCert(t *testing.T) {
t.Fail() t.Fail()
} }
info, err := bot.GetWebhookInfo()
if err != nil {
t.Error(err)
}
if info.LastErrorDate != 0 {
t.Errorf("failed to set webhook: %s", info.LastErrorMessage)
}
bot.Request(tgbotapi.RemoveWebhookConfig{}) bot.Request(tgbotapi.RemoveWebhookConfig{})
} }
@ -493,6 +503,16 @@ func TestSetWebhookWithoutCert(t *testing.T) {
t.Fail() t.Fail()
} }
info, err := bot.GetWebhookInfo()
if err != nil {
t.Error(err)
}
if info.LastErrorDate != 0 {
t.Errorf("failed to set webhook: %s", info.LastErrorMessage)
}
bot.Request(tgbotapi.RemoveWebhookConfig{}) bot.Request(tgbotapi.RemoveWebhookConfig{})
} }
@ -558,6 +578,16 @@ func ExampleNewWebhook() {
log.Fatal(err) log.Fatal(err)
} }
info, err := bot.GetWebhookInfo()
if err != nil {
log.Fatal(err)
}
if info.LastErrorDate != 0 {
log.Printf("failed to set webhook: %s", info.LastErrorMessage)
}
updates := bot.ListenForWebhook("/" + bot.Token) updates := bot.ListenForWebhook("/" + bot.Token)
go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil) go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil)
@ -566,7 +596,7 @@ func ExampleNewWebhook() {
} }
} }
func ExampleAnswerInlineQuery() { func ExampleInlineConfig() {
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") // create new bot bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") // create new bot
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)

View File

@ -720,7 +720,7 @@ type SetGameScoreConfig struct {
Score int Score int
Force bool Force bool
DisableEditMessage bool DisableEditMessage bool
ChatID int ChatID int64
ChannelUsername string ChannelUsername string
MessageID int MessageID int
InlineMessageID string InlineMessageID string
@ -733,7 +733,7 @@ func (config SetGameScoreConfig) values() (url.Values, error) {
v.Add("score", strconv.Itoa(config.Score)) v.Add("score", strconv.Itoa(config.Score))
if config.InlineMessageID == "" { if config.InlineMessageID == "" {
if config.ChannelUsername == "" { if config.ChannelUsername == "" {
v.Add("chat_id", strconv.Itoa(config.ChatID)) v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else { } else {
v.Add("chat_id", config.ChannelUsername) v.Add("chat_id", config.ChannelUsername)
} }
@ -1376,10 +1376,7 @@ func (config UnpinChatMessageConfig) values() (url.Values, error) {
// SetChatPhotoConfig allows you to set a group, supergroup, or channel's photo. // SetChatPhotoConfig allows you to set a group, supergroup, or channel's photo.
type SetChatPhotoConfig struct { type SetChatPhotoConfig struct {
ChatID int64 BaseFile
ChannelUsername string
Photo interface{}
} }
func (config SetChatPhotoConfig) method() string { func (config SetChatPhotoConfig) method() string {
@ -1390,36 +1387,12 @@ func (config SetChatPhotoConfig) name() string {
return "photo" return "photo"
} }
func (config SetChatPhotoConfig) values() (url.Values, error) {
v := url.Values{}
if config.ChannelUsername == "" {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.ChannelUsername)
}
return v, nil
}
func (config SetChatPhotoConfig) params() map[string]string {
params := make(map[string]string)
if config.ChannelUsername == "" {
params["chat_id"] = strconv.FormatInt(config.ChatID, 10)
} else {
params["chat_id"] = config.ChannelUsername
}
return params
}
func (config SetChatPhotoConfig) getFile() interface{} { func (config SetChatPhotoConfig) getFile() interface{} {
return config.Photo return config.File
} }
func (config SetChatPhotoConfig) useExistingFile() bool { func (config SetChatPhotoConfig) useExistingFile() bool {
return false return config.UseExisting
} }
// DeleteChatPhotoConfig allows you to delete a group, supergroup, or channel's photo. // DeleteChatPhotoConfig allows you to delete a group, supergroup, or channel's photo.

View File

@ -641,7 +641,7 @@ func NewCallbackWithAlert(id, text string) CallbackConfig {
} }
} }
// NewInvoice created a new Invoice request to the user. // NewInvoice creates a new Invoice request to the user.
func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices *[]LabeledPrice) InvoiceConfig { func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices *[]LabeledPrice) InvoiceConfig {
return InvoiceConfig{ return InvoiceConfig{
BaseChat: BaseChat{ChatID: chatID}, BaseChat: BaseChat{ChatID: chatID},
@ -653,3 +653,34 @@ func NewInvoice(chatID int64, title, description, payload, providerToken, startP
Currency: currency, Currency: currency,
Prices: prices} Prices: prices}
} }
// NewSetChatPhotoUpload creates a new chat photo uploader.
//
// chatID is where to send it, file is a string path to the file,
// FileReader, or FileBytes.
//
// Note that you must send animated GIFs as a document.
func NewSetChatPhotoUpload(chatID int64, file interface{}) SetChatPhotoConfig {
return SetChatPhotoConfig{
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
File: file,
UseExisting: false,
},
}
}
// NewSetChatPhotoShare shares an existing photo.
// You may use this to reshare an existing photo without reuploading it.
//
// chatID is where to send it, fileID is the ID of the file
// already uploaded.
func NewSetChatPhotoShare(chatID int64, fileID string) SetChatPhotoConfig {
return SetChatPhotoConfig{
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
FileID: fileID,
UseExisting: true,
},
}
}

View File

@ -676,7 +676,7 @@ type InlineQueryResultGame struct {
Type string `json:"type"` Type string `json:"type"`
ID string `json:"id"` ID string `json:"id"`
GameShortName string `json:"game_short_name"` GameShortName string `json:"game_short_name"`
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
} }
// ChosenInlineResult is an inline query result chosen by a User // ChosenInlineResult is an inline query result chosen by a User
@ -819,3 +819,14 @@ type InputMediaVideo struct {
Height int `json:"height,omitempty"` Height int `json:"height,omitempty"`
Duration int `json:"duration,omitempty"` Duration int `json:"duration,omitempty"`
} }
// Error is an error containing extra information returned by the Telegram API.
type Error struct {
Message string
ResponseParameters
}
// Error message string.
func (e Error) Error() string {
return e.Message
}