Merge master into develop.
commit
6a6de7e674
10
README.md
10
README.md
|
@ -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)
|
||||||
|
|
||||||
|
|
89
bot.go
89
bot.go
|
@ -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
|
|
||||||
err = json.Unmarshal(bytes, &apiResp)
|
|
||||||
if err != nil {
|
|
||||||
return APIResponse{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !apiResp.Ok {
|
if !apiResp.Ok {
|
||||||
return apiResp, errors.New(apiResp.Description)
|
var parameters ResponseParameters
|
||||||
|
|
||||||
|
if apiResp.Parameters != nil {
|
||||||
|
parameters = *apiResp.Parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiResp, Error{
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
32
bot_test.go
32
bot_test.go
|
@ -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)
|
||||||
|
|
37
configs.go
37
configs.go
|
@ -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.
|
||||||
|
|
33
helpers.go
33
helpers.go
|
@ -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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
13
types.go
13
types.go
|
@ -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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue