Merge branch 'develop' into bot-api-5.0
commit
24e02f7ba6
|
@ -3,7 +3,7 @@
|
|||
[![GoDoc](https://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api?status.svg)](http://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api)
|
||||
[![Travis](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api.svg)](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api)
|
||||
|
||||
All methods are fairly self explanatory, and reading the godoc page should
|
||||
All methods are fairly self explanatory, and reading the [godoc](http://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api) page should
|
||||
explain everything. If something isn't clear, open an issue or submit
|
||||
a pull request.
|
||||
|
||||
|
|
51
bot.go
51
bot.go
|
@ -18,6 +18,12 @@ import (
|
|||
"github.com/technoweenie/multipartstreamer"
|
||||
)
|
||||
|
||||
// HTTPClient is the type needed for the bot to perform HTTP requests.
|
||||
type HTTPClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
PostForm(url string, data url.Values) (*http.Response, error)
|
||||
}
|
||||
|
||||
// BotAPI allows you to interact with the Telegram Bot API.
|
||||
type BotAPI struct {
|
||||
Token string `json:"token"`
|
||||
|
@ -25,7 +31,7 @@ type BotAPI struct {
|
|||
Buffer int `json:"buffer"`
|
||||
|
||||
Self User `json:"-"`
|
||||
Client *http.Client `json:"-"`
|
||||
Client HTTPClient `json:"-"`
|
||||
shutdownChannel chan interface{}
|
||||
|
||||
apiEndpoint string
|
||||
|
@ -35,21 +41,29 @@ type BotAPI struct {
|
|||
//
|
||||
// It requires a token, provided by @BotFather on Telegram.
|
||||
func NewBotAPI(token string) (*BotAPI, error) {
|
||||
return NewBotAPIWithClient(token, &http.Client{})
|
||||
return NewBotAPIWithClient(token, APIEndpoint, &http.Client{})
|
||||
}
|
||||
|
||||
// NewBotAPIWithAPIEndpoint creates a new BotAPI instance
|
||||
// and allows you to pass API endpoint.
|
||||
//
|
||||
// It requires a token, provided by @BotFather on Telegram and API endpoint.
|
||||
func NewBotAPIWithAPIEndpoint(token, apiEndpoint string) (*BotAPI, error) {
|
||||
return NewBotAPIWithClient(token, apiEndpoint, &http.Client{})
|
||||
}
|
||||
|
||||
// NewBotAPIWithClient creates a new BotAPI instance
|
||||
// and allows you to pass a http.Client.
|
||||
//
|
||||
// It requires a token, provided by @BotFather on Telegram.
|
||||
func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
|
||||
// It requires a token, provided by @BotFather on Telegram and API endpoint.
|
||||
func NewBotAPIWithClient(token, apiEndpoint string, client HTTPClient) (*BotAPI, error) {
|
||||
bot := &BotAPI{
|
||||
Token: token,
|
||||
Client: client,
|
||||
Buffer: 100,
|
||||
shutdownChannel: make(chan interface{}),
|
||||
|
||||
apiEndpoint: APIEndpoint,
|
||||
apiEndpoint: apiEndpoint,
|
||||
}
|
||||
|
||||
self, err := bot.GetMe()
|
||||
|
@ -413,6 +427,7 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) UpdatesChannel {
|
|||
for {
|
||||
select {
|
||||
case <-bot.shutdownChannel:
|
||||
close(ch)
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
@ -451,21 +466,35 @@ func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
|
|||
ch := make(chan Update, bot.Buffer)
|
||||
|
||||
http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
ch <- bot.HandleUpdate(w, r)
|
||||
update, err := bot.HandleUpdate(r)
|
||||
if err != nil {
|
||||
errMsg, _ := json.Marshal(map[string]string{"error": err.Error()})
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write(errMsg)
|
||||
return
|
||||
}
|
||||
|
||||
ch <- *update
|
||||
})
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
// HandleUpdate parses and returns update received via webhook
|
||||
func (bot *BotAPI) HandleUpdate(res http.ResponseWriter, req *http.Request) Update {
|
||||
bytes, _ := ioutil.ReadAll(req.Body)
|
||||
req.Body.Close()
|
||||
func (bot *BotAPI) HandleUpdate(r *http.Request) (*Update, error) {
|
||||
if r.Method != http.MethodPost {
|
||||
err := errors.New("wrong HTTP method required POST")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var update Update
|
||||
json.Unmarshal(bytes, &update)
|
||||
err := json.NewDecoder(r.Body).Decode(&update)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return update
|
||||
return &update, nil
|
||||
}
|
||||
|
||||
// WriteToHTTPResponse writes the request to the HTTP ResponseWriter.
|
||||
|
|
48
bot_test.go
48
bot_test.go
|
@ -23,10 +23,25 @@ const (
|
|||
ExistingStickerFileID = "BQADAgADcwADjMcoCbdl-6eB--YPAg"
|
||||
)
|
||||
|
||||
type testLogger struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (t testLogger) Println(v ...interface{}) {
|
||||
t.t.Log(v...)
|
||||
}
|
||||
|
||||
func (t testLogger) Printf(format string, v ...interface{}) {
|
||||
t.t.Logf(format, v...)
|
||||
}
|
||||
|
||||
func getBot(t *testing.T) (*BotAPI, error) {
|
||||
bot, err := NewBotAPI(TestToken)
|
||||
bot.Debug = true
|
||||
|
||||
logger := testLogger{t}
|
||||
SetLogger(logger)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
@ -417,6 +432,32 @@ func TestSendWithExistingStickerAndKeyboardHide(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSendWithDice(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := NewDice(ChatID)
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSendWithDiceWithEmoji(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := NewDiceWithEmoji(ChatID, "🏀")
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetFile(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
|
@ -634,7 +675,12 @@ func ExampleWebhookHandler() {
|
|||
}
|
||||
|
||||
http.HandleFunc("/"+bot.Token, func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%+v\n", bot.HandleUpdate(w, r))
|
||||
update, err := bot.HandleUpdate(r)
|
||||
if err != nil {
|
||||
log.Printf("%+v\n", err.Error())
|
||||
} else {
|
||||
log.Printf("%+v\n", *update)
|
||||
}
|
||||
})
|
||||
|
||||
go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil)
|
||||
|
|
99
configs.go
99
configs.go
|
@ -1001,12 +1001,9 @@ func (config InlineConfig) params() (Params, error) {
|
|||
params.AddNonEmpty("next_offset", config.NextOffset)
|
||||
params.AddNonEmpty("switch_pm_text", config.SwitchPMText)
|
||||
params.AddNonEmpty("switch_pm_parameter", config.SwitchPMParameter)
|
||||
err := params.AddInterface("results", config.Results)
|
||||
|
||||
if err := params.AddInterface("results", config.Results); err != nil {
|
||||
return params, err
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// CallbackConfig contains information on making a CallbackQuery response.
|
||||
|
@ -1100,12 +1097,10 @@ func (config RestrictChatMemberConfig) params() (Params, error) {
|
|||
params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername)
|
||||
params.AddNonZero("user_id", config.UserID)
|
||||
|
||||
if err := params.AddInterface("permissions", config.Permissions); err != nil {
|
||||
return params, err
|
||||
}
|
||||
err := params.AddInterface("permissions", config.Permissions)
|
||||
params.AddNonZero64("until_date", config.UntilDate)
|
||||
|
||||
return params, nil
|
||||
return params, err
|
||||
}
|
||||
|
||||
// PromoteChatMemberConfig contains fields to promote members of chat
|
||||
|
@ -1327,10 +1322,7 @@ func (config InvoiceConfig) params() (Params, error) {
|
|||
params["start_parameter"] = config.StartParameter
|
||||
params["currency"] = config.Currency
|
||||
|
||||
if err = params.AddInterface("prices", config.Prices); err != nil {
|
||||
return params, err
|
||||
}
|
||||
|
||||
err = params.AddInterface("prices", config.Prices)
|
||||
params.AddNonEmpty("provider_data", config.ProviderData)
|
||||
params.AddNonEmpty("photo_url", config.PhotoURL)
|
||||
params.AddNonZero("photo_size", config.PhotoSize)
|
||||
|
@ -1344,7 +1336,7 @@ func (config InvoiceConfig) params() (Params, error) {
|
|||
params.AddBool("send_phone_number_to_provider", config.SendPhoneNumberToProvider)
|
||||
params.AddBool("send_email_to_provider", config.SendEmailToProvider)
|
||||
|
||||
return params, nil
|
||||
return params, err
|
||||
}
|
||||
|
||||
func (config InvoiceConfig) method() string {
|
||||
|
@ -1359,6 +1351,21 @@ type ShippingConfig struct {
|
|||
ErrorMessage string
|
||||
}
|
||||
|
||||
func (config ShippingConfig) method() string {
|
||||
return "answerShippingQuery"
|
||||
}
|
||||
|
||||
func (config ShippingConfig) params() (Params, error) {
|
||||
params := make(Params)
|
||||
|
||||
params["shipping_query_id"] = config.ShippingQueryID
|
||||
params.AddBool("ok", config.OK)
|
||||
err := params.AddInterface("shipping_options", config.ShippingOptions)
|
||||
params.AddNonEmpty("error_message", config.ErrorMessage)
|
||||
|
||||
return params, err
|
||||
}
|
||||
|
||||
// PreCheckoutConfig conatins information for answerPreCheckoutQuery request.
|
||||
type PreCheckoutConfig struct {
|
||||
PreCheckoutQueryID string // required
|
||||
|
@ -1366,6 +1373,20 @@ type PreCheckoutConfig struct {
|
|||
ErrorMessage string
|
||||
}
|
||||
|
||||
func (config PreCheckoutConfig) method() string {
|
||||
return "answerPreCheckoutQuery"
|
||||
}
|
||||
|
||||
func (config PreCheckoutConfig) params() (Params, error) {
|
||||
params := make(Params)
|
||||
|
||||
params["pre_checkout_query_id"] = config.PreCheckoutQueryID
|
||||
params.AddBool("ok", config.OK)
|
||||
params.AddNonEmpty("error_message", config.ErrorMessage)
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// DeleteMessageConfig contains information of a message in a chat to delete.
|
||||
type DeleteMessageConfig struct {
|
||||
ChannelUsername string
|
||||
|
@ -1828,31 +1849,6 @@ func (config MediaGroupConfig) params() (Params, error) {
|
|||
return params, nil
|
||||
}
|
||||
|
||||
// DiceConfig allows you to send a random dice roll to Telegram.
|
||||
//
|
||||
// Emoji may be one of the following: 🎲 (1-6), 🎯 (1-6), 🏀 (1-5), ⚽ (1-5),
|
||||
// 🎰 (1-64).
|
||||
type DiceConfig struct {
|
||||
BaseChat
|
||||
|
||||
Emoji string
|
||||
}
|
||||
|
||||
func (config DiceConfig) method() string {
|
||||
return "sendDice"
|
||||
}
|
||||
|
||||
func (config DiceConfig) params() (Params, error) {
|
||||
params, err := config.BaseChat.params()
|
||||
if err != nil {
|
||||
return params, err
|
||||
}
|
||||
|
||||
params.AddNonEmpty("emoji", config.Emoji)
|
||||
|
||||
return params, err
|
||||
}
|
||||
|
||||
// GetMyCommandsConfig gets a list of the currently registered commands.
|
||||
type GetMyCommandsConfig struct{}
|
||||
|
||||
|
@ -1861,7 +1857,7 @@ func (config GetMyCommandsConfig) method() string {
|
|||
}
|
||||
|
||||
func (config GetMyCommandsConfig) params() (Params, error) {
|
||||
return make(Params), nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SetMyCommandsConfig sets a list of commands the bot understands.
|
||||
|
@ -1880,3 +1876,28 @@ func (config SetMyCommandsConfig) params() (Params, error) {
|
|||
|
||||
return params, err
|
||||
}
|
||||
|
||||
// DiceConfig contains information about a sendDice request.
|
||||
type DiceConfig struct {
|
||||
BaseChat
|
||||
// Emoji on which the dice throw animation is based.
|
||||
// Currently, must be one of “🎲”, “🎯”, or “🏀”.
|
||||
// Dice can have values 1-6 for “🎲” and “🎯”, and values 1-5 for “🏀”.
|
||||
// Defaults to “🎲”
|
||||
Emoji string
|
||||
}
|
||||
|
||||
func (config DiceConfig) method() string {
|
||||
return "sendDice"
|
||||
}
|
||||
|
||||
func (config DiceConfig) params() (Params, error) {
|
||||
params, err := config.BaseChat.params()
|
||||
if err != nil {
|
||||
return params, err
|
||||
}
|
||||
|
||||
params.AddNonEmpty("emoji", config.Emoji)
|
||||
|
||||
return params, err
|
||||
}
|
||||
|
|
36
helpers.go
36
helpers.go
|
@ -29,7 +29,8 @@ func NewDeleteMessage(chatID int64, messageID int) DeleteMessageConfig {
|
|||
// NewMessageToChannel creates a new Message that is sent to a channel
|
||||
// by username.
|
||||
//
|
||||
// username is the username of the channel, text is the message text.
|
||||
// username is the username of the channel, text is the message text,
|
||||
// and the username should be in the form of `@username`.
|
||||
func NewMessageToChannel(username string, text string) MessageConfig {
|
||||
return MessageConfig{
|
||||
BaseChat: BaseChat{
|
||||
|
@ -527,7 +528,7 @@ func NewInlineQueryResultCachedGIF(id, gifID string) InlineQueryResultCachedGIF
|
|||
return InlineQueryResultCachedGIF{
|
||||
Type: "gif",
|
||||
ID: id,
|
||||
GifID: gifID,
|
||||
GIFID: gifID,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -540,12 +541,12 @@ func NewInlineQueryResultMPEG4GIF(id, url string) InlineQueryResultMPEG4GIF {
|
|||
}
|
||||
}
|
||||
|
||||
// NewInlineQueryResultCachedMPEG4GIF create a new inline query with cached photo.
|
||||
func NewInlineQueryResultCachedMPEG4GIF(id, MPEG4GifID string) InlineQueryResultCachedMpeg4Gif {
|
||||
return InlineQueryResultCachedMpeg4Gif{
|
||||
// NewInlineQueryResultCachedMPEG4GIF create a new inline query with cached MPEG4 GIF.
|
||||
func NewInlineQueryResultCachedMPEG4GIF(id, MPEG4GIFID string) InlineQueryResultCachedMPEG4GIF {
|
||||
return InlineQueryResultCachedMPEG4GIF{
|
||||
Type: "mpeg4_gif",
|
||||
ID: id,
|
||||
MGifID: MPEG4GifID,
|
||||
MPEG4FileID: MPEG4GIFID,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -700,6 +701,18 @@ func NewEditMessageText(chatID int64, messageID int, text string) EditMessageTex
|
|||
}
|
||||
}
|
||||
|
||||
// NewEditMessageTextAndMarkup allows you to edit the text and replymarkup of a message.
|
||||
func NewEditMessageTextAndMarkup(chatID int64, messageID int, text string, replyMarkup InlineKeyboardMarkup) EditMessageTextConfig {
|
||||
return EditMessageTextConfig{
|
||||
BaseEdit: BaseEdit{
|
||||
ChatID: chatID,
|
||||
MessageID: messageID,
|
||||
ReplyMarkup: &replyMarkup,
|
||||
},
|
||||
Text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEditMessageCaption allows you to edit the caption of a message.
|
||||
func NewEditMessageCaption(chatID int64, messageID int, caption string) EditMessageCaptionConfig {
|
||||
return EditMessageCaptionConfig{
|
||||
|
@ -723,17 +736,6 @@ func NewEditMessageReplyMarkup(chatID int64, messageID int, replyMarkup InlineKe
|
|||
}
|
||||
}
|
||||
|
||||
// NewHideKeyboard hides the keyboard, with the option for being selective
|
||||
// or hiding for everyone.
|
||||
func NewHideKeyboard(selective bool) ReplyKeyboardHide {
|
||||
log.Println("NewHideKeyboard is deprecated, please use NewRemoveKeyboard")
|
||||
|
||||
return ReplyKeyboardHide{
|
||||
HideKeyboard: true,
|
||||
Selective: selective,
|
||||
}
|
||||
}
|
||||
|
||||
// NewRemoveKeyboard hides the keyboard, with the option for being selective
|
||||
// or hiding for everyone.
|
||||
func NewRemoveKeyboard(selective bool) ReplyKeyboardRemove {
|
||||
|
|
|
@ -174,3 +174,21 @@ func TestNewEditMessageReplyMarkup(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNewDice(t *testing.T) {
|
||||
dice := NewDice(42)
|
||||
|
||||
if dice.ChatID != 42 ||
|
||||
dice.Emoji != "" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDiceWithEmoji(t *testing.T) {
|
||||
dice := NewDiceWithEmoji(42, "🏀")
|
||||
|
||||
if dice.ChatID != 42 ||
|
||||
dice.Emoji != "🏀" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -310,6 +310,7 @@ var (
|
|||
_ Chattable = MessageConfig{}
|
||||
_ Chattable = PhotoConfig{}
|
||||
_ Chattable = PinChatMessageConfig{}
|
||||
_ Chattable = PreCheckoutConfig{}
|
||||
_ Chattable = PromoteChatMemberConfig{}
|
||||
_ Chattable = DeleteWebhookConfig{}
|
||||
_ Chattable = RestrictChatMemberConfig{}
|
||||
|
@ -318,6 +319,7 @@ var (
|
|||
_ Chattable = SetChatPhotoConfig{}
|
||||
_ Chattable = SetChatTitleConfig{}
|
||||
_ Chattable = SetGameScoreConfig{}
|
||||
_ Chattable = ShippingConfig{}
|
||||
_ Chattable = StickerConfig{}
|
||||
_ Chattable = StopPollConfig{}
|
||||
_ Chattable = StopMessageLiveLocationConfig{}
|
||||
|
|
Loading…
Reference in New Issue