Unify params and values, introduce helpers to deal with fields.

bot-api-6.1
Syfaro 2018-10-08 18:21:29 -05:00
parent 9d2d117c0e
commit 03815bf5bd
5 changed files with 547 additions and 904 deletions

219
bot.go
View File

@ -12,7 +12,6 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"strconv"
"strings" "strings"
"time" "time"
@ -59,15 +58,31 @@ func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
return bot, nil return bot, nil
} }
func buildParams(in Params) (out url.Values) {
if in == nil {
return url.Values{}
}
out = url.Values{}
for key, value := range in {
out.Set(key, value)
}
return
}
// MakeRequest makes a request to a specific endpoint with our token. // MakeRequest makes a request to a specific endpoint with our token.
func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) { func (bot *BotAPI) MakeRequest(endpoint string, params Params) (APIResponse, error) {
if bot.Debug { if bot.Debug {
log.Printf("Endpoint: %s, values: %v\n", endpoint, params) log.Printf("Endpoint: %s, params: %v\n", endpoint, params)
} }
method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint) method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
resp, err := bot.Client.PostForm(method, params) values := buildParams(params)
resp, err := bot.Client.PostForm(method, values)
if err != nil { if err != nil {
return APIResponse{}, err return APIResponse{}, err
} }
@ -131,7 +146,7 @@ func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse)
// //
// Note that if your FileReader has a size set to -1, it will read // Note that if your FileReader has a size set to -1, it will read
// the file into memory to calculate a size. // the file into memory to calculate a size.
func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) { func (bot *BotAPI) UploadFile(endpoint string, params Params, fieldname string, file interface{}) (APIResponse, error) {
ms := multipartstreamer.New() ms := multipartstreamer.New()
switch f := file.(type) { switch f := file.(type) {
@ -261,30 +276,20 @@ func (bot *BotAPI) IsMessageToMe(message Message) bool {
// Request sends a Chattable to Telegram, and returns the APIResponse. // Request sends a Chattable to Telegram, and returns the APIResponse.
func (bot *BotAPI) Request(c Chattable) (APIResponse, error) { func (bot *BotAPI) Request(c Chattable) (APIResponse, error) {
params, err := c.params()
if err != nil {
return APIResponse{}, err
}
switch t := c.(type) { switch t := c.(type) {
case Fileable: case Fileable:
if t.useExistingFile() { if t.useExistingFile() {
v, err := t.values() return bot.MakeRequest(t.method(), params)
if err != nil {
return APIResponse{}, err
} }
return bot.MakeRequest(t.method(), v) return bot.UploadFile(t.method(), params, t.name(), t.getFile())
}
p, err := t.params()
if err != nil {
return APIResponse{}, err
}
return bot.UploadFile(t.method(), p, t.name(), t.getFile())
default: default:
v, err := c.values() return bot.MakeRequest(c.method(), params)
if err != nil {
return APIResponse{}, err
}
return bot.MakeRequest(c.method(), v)
} }
} }
@ -307,16 +312,9 @@ func (bot *BotAPI) Send(c Chattable) (Message, error) {
// It requires UserID. // It requires UserID.
// Offset and Limit are optional. // Offset and Limit are optional.
func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) { func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
v := url.Values{} params, _ := config.params()
v.Add("user_id", strconv.Itoa(config.UserID))
if config.Offset != 0 {
v.Add("offset", strconv.Itoa(config.Offset))
}
if config.Limit != 0 {
v.Add("limit", strconv.Itoa(config.Limit))
}
resp, err := bot.MakeRequest("getUserProfilePhotos", v) resp, err := bot.MakeRequest(config.method(), params)
if err != nil { if err != nil {
return UserProfilePhotos{}, err return UserProfilePhotos{}, err
} }
@ -331,8 +329,9 @@ func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserPro
// //
// Requires FileID. // Requires FileID.
func (bot *BotAPI) GetFile(config FileConfig) (File, error) { func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
v := url.Values{} v := make(Params)
v.Add("file_id", config.FileID)
v["file_id"] = config.FileID
resp, err := bot.MakeRequest("getFile", v) resp, err := bot.MakeRequest("getFile", v)
if err != nil { if err != nil {
@ -353,18 +352,9 @@ func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
// Set Timeout to a large number to reduce requests so you can get updates // Set Timeout to a large number to reduce requests so you can get updates
// instantly instead of having to wait between requests. // instantly instead of having to wait between requests.
func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) { func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
v := url.Values{} params, _ := config.params()
if config.Offset != 0 {
v.Add("offset", strconv.Itoa(config.Offset))
}
if config.Limit > 0 {
v.Add("limit", strconv.Itoa(config.Limit))
}
if config.Timeout > 0 {
v.Add("timeout", strconv.Itoa(config.Timeout))
}
resp, err := bot.MakeRequest("getUpdates", v) resp, err := bot.MakeRequest(config.method(), params)
if err != nil { if err != nil {
return []Update{}, err return []Update{}, err
} }
@ -378,7 +368,7 @@ func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
// GetWebhookInfo allows you to fetch information about a webhook and if // GetWebhookInfo allows you to fetch information about a webhook and if
// one currently is set, along with pending update count and error messages. // one currently is set, along with pending update count and error messages.
func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) { func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) {
resp, err := bot.MakeRequest("getWebhookInfo", url.Values{}) resp, err := bot.MakeRequest("getWebhookInfo", nil)
if err != nil { if err != nil {
return WebhookInfo{}, err return WebhookInfo{}, err
} }
@ -448,13 +438,9 @@ func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
// GetChat gets information about a chat. // GetChat gets information about a chat.
func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) { func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) {
v := url.Values{} v := make(Params)
if config.SuperGroupUsername == "" { v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername)
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
resp, err := bot.MakeRequest("getChat", v) resp, err := bot.MakeRequest("getChat", v)
if err != nil { if err != nil {
@ -472,13 +458,9 @@ func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) {
// If none have been appointed, only the creator will be returned. // If none have been appointed, only the creator will be returned.
// Bots are not shown, even if they are an administrator. // Bots are not shown, even if they are an administrator.
func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error) { func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error) {
v := url.Values{} v := make(Params)
if config.SuperGroupUsername == "" { v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername)
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
resp, err := bot.MakeRequest("getChatAdministrators", v) resp, err := bot.MakeRequest("getChatAdministrators", v)
if err != nil { if err != nil {
@ -493,13 +475,9 @@ func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error
// GetChatMembersCount gets the number of users in a chat. // GetChatMembersCount gets the number of users in a chat.
func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) { func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) {
v := url.Values{} v := make(Params)
if config.SuperGroupUsername == "" { v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername)
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
resp, err := bot.MakeRequest("getChatMembersCount", v) resp, err := bot.MakeRequest("getChatMembersCount", v)
if err != nil { if err != nil {
@ -514,14 +492,10 @@ func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) {
// GetChatMember gets a specific chat member. // GetChatMember gets a specific chat member.
func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) { func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) {
v := url.Values{} v := make(Params)
if config.SuperGroupUsername == "" { v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername)
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) v.AddNonZero("user_id", config.UserID)
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
v.Add("user_id", strconv.Itoa(config.UserID))
resp, err := bot.MakeRequest("getChatMember", v) resp, err := bot.MakeRequest("getChatMember", v)
if err != nil { if err != nil {
@ -537,16 +511,10 @@ func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error)
// UnbanChatMember unbans a user from a chat. Note that this only will work // UnbanChatMember unbans a user from a chat. Note that this only will work
// in supergroups and channels, and requires the bot to be an admin. // in supergroups and channels, and requires the bot to be an admin.
func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error) { func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error) {
v := url.Values{} v := make(Params)
if config.SuperGroupUsername != "" { v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername)
v.Add("chat_id", config.SuperGroupUsername) v.AddNonZero("user_id", config.UserID)
} else if config.ChannelUsername != "" {
v.Add("chat_id", config.ChannelUsername)
} else {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
}
v.Add("user_id", strconv.Itoa(config.UserID))
return bot.MakeRequest("unbanChatMember", v) return bot.MakeRequest("unbanChatMember", v)
} }
@ -556,80 +524,45 @@ func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error)
//appropriate admin rights. Pass True for all boolean parameters to lift //appropriate admin rights. Pass True for all boolean parameters to lift
//restrictions from a user. Returns True on success. //restrictions from a user. Returns True on success.
func (bot *BotAPI) RestrictChatMember(config RestrictChatMemberConfig) (APIResponse, error) { func (bot *BotAPI) RestrictChatMember(config RestrictChatMemberConfig) (APIResponse, error) {
v := url.Values{} v := make(Params)
if config.SuperGroupUsername != "" { v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername)
v.Add("chat_id", config.SuperGroupUsername) v.AddNonZero("user_id", config.UserID)
} else if config.ChannelUsername != "" {
v.Add("chat_id", config.ChannelUsername)
} else {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
}
v.Add("user_id", strconv.Itoa(config.UserID))
if config.CanSendMessages != nil { v.AddNonNilBool("can_send_messages", config.CanSendMessages)
v.Add("can_send_messages", strconv.FormatBool(*config.CanSendMessages)) v.AddNonNilBool("can_send_media_messages", config.CanSendMediaMessages)
} v.AddNonNilBool("can_send_other_messages", config.CanSendOtherMessages)
if config.CanSendMediaMessages != nil { v.AddNonNilBool("can_add_web_page_previews", config.CanAddWebPagePreviews)
v.Add("can_send_media_messages", strconv.FormatBool(*config.CanSendMediaMessages)) v.AddNonZero64("until_date", config.UntilDate)
}
if config.CanSendOtherMessages != nil {
v.Add("can_send_other_messages", strconv.FormatBool(*config.CanSendOtherMessages))
}
if config.CanAddWebPagePreviews != nil {
v.Add("can_add_web_page_previews", strconv.FormatBool(*config.CanAddWebPagePreviews))
}
if config.UntilDate != 0 {
v.Add("until_date", strconv.FormatInt(config.UntilDate, 10))
}
return bot.MakeRequest("restrictChatMember", v) return bot.MakeRequest("restrictChatMember", v)
} }
// PromoteChatMember add admin rights to user // PromoteChatMember add admin rights to user
func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) { func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) {
v := url.Values{} v := make(Params)
if config.SuperGroupUsername != "" { v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername)
v.Add("chat_id", config.SuperGroupUsername) v.AddNonZero("user_id", config.UserID)
} else if config.ChannelUsername != "" {
v.Add("chat_id", config.ChannelUsername)
} else {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
}
v.Add("user_id", strconv.Itoa(config.UserID))
if config.CanChangeInfo != nil { v.AddNonNilBool("can_change_info", config.CanChangeInfo)
v.Add("can_change_info", strconv.FormatBool(*config.CanChangeInfo)) v.AddNonNilBool("can_post_messages", config.CanPostMessages)
} v.AddNonNilBool("can_edit_messages", config.CanEditMessages)
if config.CanPostMessages != nil { v.AddNonNilBool("can_delete_messages", config.CanDeleteMessages)
v.Add("can_post_messages", strconv.FormatBool(*config.CanPostMessages)) v.AddNonNilBool("can_invite_members", config.CanInviteUsers)
} v.AddNonNilBool("can_restrict_members", config.CanRestrictMembers)
if config.CanEditMessages != nil { v.AddNonNilBool("can_pin_messages", config.CanPinMessages)
v.Add("can_edit_messages", strconv.FormatBool(*config.CanEditMessages)) v.AddNonNilBool("can_promote_members", config.CanPromoteMembers)
}
if config.CanDeleteMessages != nil {
v.Add("can_delete_messages", strconv.FormatBool(*config.CanDeleteMessages))
}
if config.CanInviteUsers != nil {
v.Add("can_invite_users", strconv.FormatBool(*config.CanInviteUsers))
}
if config.CanRestrictMembers != nil {
v.Add("can_restrict_members", strconv.FormatBool(*config.CanRestrictMembers))
}
if config.CanPinMessages != nil {
v.Add("can_pin_messages", strconv.FormatBool(*config.CanPinMessages))
}
if config.CanPromoteMembers != nil {
v.Add("can_promote_members", strconv.FormatBool(*config.CanPromoteMembers))
}
return bot.MakeRequest("promoteChatMember", v) return bot.MakeRequest("promoteChatMember", v)
} }
// GetGameHighScores allows you to get the high scores for a game. // GetGameHighScores allows you to get the high scores for a game.
func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) { func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) {
v, _ := config.values() v, err := config.params()
if err != nil {
return nil, err
}
resp, err := bot.MakeRequest(config.method(), v) resp, err := bot.MakeRequest(config.method(), v)
if err != nil { if err != nil {
@ -644,13 +577,9 @@ func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHigh
// GetInviteLink get InviteLink for a chat // GetInviteLink get InviteLink for a chat
func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) { func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) {
v := url.Values{} v := make(Params)
if config.SuperGroupUsername == "" { v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername)
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
resp, err := bot.MakeRequest("exportChatInviteLink", v) resp, err := bot.MakeRequest("exportChatInviteLink", v)
if err != nil { if err != nil {
@ -665,7 +594,7 @@ func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) {
// GetStickerSet returns a StickerSet. // GetStickerSet returns a StickerSet.
func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) { func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) {
v, err := config.values() v, err := config.params()
if err != nil { if err != nil {
return StickerSet{}, nil return StickerSet{}, nil
} }

1113
configs.go

File diff suppressed because it is too large Load Diff

100
params.go 100644
View File

@ -0,0 +1,100 @@
package tgbotapi
import (
"encoding/json"
"reflect"
"strconv"
)
// Params represents a set of parameters that gets passed to a request.
type Params map[string]string
// AddNonEmpty adds a value if it not an empty string.
func (p Params) AddNonEmpty(key, value string) {
if value != "" {
p[key] = value
}
}
// AddNonZero adds a value if it is not zero.
func (p Params) AddNonZero(key string, value int) {
if value != 0 {
p[key] = strconv.Itoa(value)
}
}
// AddNonZero64 is the same as AddNonZero except uses an int64.
func (p Params) AddNonZero64(key string, value int64) {
if value != 0 {
p[key] = strconv.FormatInt(value, 10)
}
}
// AddBool adds a value of a bool if it is true.
func (p Params) AddBool(key string, value bool) {
if value {
p[key] = strconv.FormatBool(value)
}
}
// AddNonNilBool adds the value of a bool pointer if not nil.
func (p Params) AddNonNilBool(key string, value *bool) {
if value != nil {
p[key] = strconv.FormatBool(*value)
}
}
// AddNonZeroFloat adds a floating point value that is not zero.
func (p Params) AddNonZeroFloat(key string, value float64) {
if value != 0 {
p[key] = strconv.FormatFloat(value, 'f', 6, 64)
}
}
// AddInterface adds an interface if it is not nill and can be JSON marshalled.
func (p Params) AddInterface(key string, value interface{}) error {
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
return nil
}
b, err := json.Marshal(value)
if err != nil {
return err
}
p[key] = string(b)
return nil
}
// AddFirstValid attempts to add the first item that is not a default value.
//
// For example, AddFirstValid(0, "", "test") would add "test".
func (p Params) AddFirstValid(key string, args ...interface{}) error {
for _, arg := range args {
switch v := arg.(type) {
case int:
if v != 0 {
p[key] = strconv.Itoa(v)
}
case int64:
if v != 0 {
p[key] = strconv.FormatInt(v, 10)
}
case string:
if v != "" {
p[key] = v
}
case nil:
default:
b, err := json.Marshal(arg)
if err != nil {
return err
}
p[key] = string(b)
}
}
return nil
}

View File

@ -307,7 +307,7 @@ type (
MiddleNameNative string `json:"middle_name_native"` MiddleNameNative string `json:"middle_name_native"`
} }
// IdDocumentData https://core.telegram.org/passport#iddocumentdata // IDDocumentData https://core.telegram.org/passport#iddocumentdata
IDDocumentData struct { IDDocumentData struct {
DocumentNumber string `json:"document_no"` DocumentNumber string `json:"document_no"`
ExpiryDate string `json:"expiry_date"` ExpiryDate string `json:"expiry_date"`

View File

@ -198,3 +198,44 @@ func TestFileLink(t *testing.T) {
t.Fail() t.Fail()
} }
} }
// Ensure all configs are sendable
var (
_ tgbotapi.Chattable = tgbotapi.AnimationConfig{}
_ tgbotapi.Chattable = tgbotapi.AudioConfig{}
_ tgbotapi.Chattable = tgbotapi.CallbackConfig{}
_ tgbotapi.Chattable = tgbotapi.ChatActionConfig{}
_ tgbotapi.Chattable = tgbotapi.ContactConfig{}
_ tgbotapi.Chattable = tgbotapi.DeleteChatPhotoConfig{}
_ tgbotapi.Chattable = tgbotapi.DeleteChatStickerSetConfig{}
_ tgbotapi.Chattable = tgbotapi.DeleteMessageConfig{}
_ tgbotapi.Chattable = tgbotapi.DocumentConfig{}
_ tgbotapi.Chattable = tgbotapi.EditMessageCaptionConfig{}
_ tgbotapi.Chattable = tgbotapi.EditMessageLiveLocationConfig{}
_ tgbotapi.Chattable = tgbotapi.EditMessageReplyMarkupConfig{}
_ tgbotapi.Chattable = tgbotapi.EditMessageTextConfig{}
_ tgbotapi.Chattable = tgbotapi.ForwardConfig{}
_ tgbotapi.Chattable = tgbotapi.GameConfig{}
_ tgbotapi.Chattable = tgbotapi.GetGameHighScoresConfig{}
_ tgbotapi.Chattable = tgbotapi.InlineConfig{}
_ tgbotapi.Chattable = tgbotapi.InvoiceConfig{}
_ tgbotapi.Chattable = tgbotapi.KickChatMemberConfig{}
_ tgbotapi.Chattable = tgbotapi.LocationConfig{}
_ tgbotapi.Chattable = tgbotapi.MediaGroupConfig{}
_ tgbotapi.Chattable = tgbotapi.MessageConfig{}
_ tgbotapi.Chattable = tgbotapi.PhotoConfig{}
_ tgbotapi.Chattable = tgbotapi.PinChatMessageConfig{}
_ tgbotapi.Chattable = tgbotapi.SetChatDescriptionConfig{}
_ tgbotapi.Chattable = tgbotapi.SetChatPhotoConfig{}
_ tgbotapi.Chattable = tgbotapi.SetChatTitleConfig{}
_ tgbotapi.Chattable = tgbotapi.SetGameScoreConfig{}
_ tgbotapi.Chattable = tgbotapi.StickerConfig{}
_ tgbotapi.Chattable = tgbotapi.UnpinChatMessageConfig{}
_ tgbotapi.Chattable = tgbotapi.UpdateConfig{}
_ tgbotapi.Chattable = tgbotapi.UserProfilePhotosConfig{}
_ tgbotapi.Chattable = tgbotapi.VenueConfig{}
_ tgbotapi.Chattable = tgbotapi.VideoConfig{}
_ tgbotapi.Chattable = tgbotapi.VideoNoteConfig{}
_ tgbotapi.Chattable = tgbotapi.VoiceConfig{}
_ tgbotapi.Chattable = tgbotapi.WebhookConfig{}
)