Compare commits

..

2 Commits

Author SHA1 Message Date
Павлюченко Станислав d576cafba0 go mod 6 years ago
Syfaro 9860bdfd3a Fix capitalization in doc. 7 years ago
  1. 57
      README.md
  2. 627
      bot.go
  3. 238
      bot_test.go
  4. 1463
      configs.go
  5. 4
      go.mod
  6. 106
      helpers.go
  7. 52
      helpers_test.go
  8. 100
      params.go
  9. 252
      types.go
  10. 113
      types_test.go

@ -62,7 +62,60 @@ func main() {
} }
``` ```
There are more examples on the [site](https://go-telegram-bot-api.github.io/) There are more examples on the [wiki](https://github.com/go-telegram-bot-api/telegram-bot-api/wiki)
with detailed information on how to do many different kinds of things. with detailed information on how to do many differen kinds of things.
It's a great place to get started on using keyboards, commands, or other It's a great place to get started on using keyboards, commands, or other
kinds of reply markup. kinds of reply markup.
If you need to use webhooks (if you wish to run on Google App Engine),
you may use a slightly different method.
```go
package main
import (
"log"
"net/http"
"github.com/go-telegram-bot-api/telegram-bot-api"
)
func main() {
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken")
if err != nil {
log.Fatal(err)
}
bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName)
_, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem"))
if err != nil {
log.Fatal(err)
}
info, err := bot.GetWebhookInfo()
if err != nil {
log.Fatal(err)
}
if info.LastErrorDate != 0 {
log.Printf("Telegram callback failed: %s", info.LastErrorMessage)
}
updates := bot.ListenForWebhook("/" + bot.Token)
go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil)
for update := range updates {
log.Printf("%+v\n", update)
}
}
```
If you need, you may generate a self signed certficate, as this requires
HTTPS / TLS. The above example tells Telegram that this is your
certificate and that it should be trusted, even though it is not
properly signed.
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3560 -subj "//O=Org\CN=Test" -nodes
Now that [Let's Encrypt](https://letsencrypt.org) is available,
you may wish to generate your free TLS certificate there.

627
bot.go

@ -12,6 +12,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"strconv"
"strings" "strings"
"time" "time"
@ -24,8 +25,8 @@ type BotAPI struct {
Debug bool `json:"debug"` Debug bool `json:"debug"`
Buffer int `json:"buffer"` Buffer int `json:"buffer"`
Self User `json:"-"` Self User `json:"-"`
Client *http.Client `json:"-"` Client *http.Client `json:"-"`
shutdownChannel chan interface{} shutdownChannel chan interface{}
} }
@ -42,9 +43,9 @@ func NewBotAPI(token string) (*BotAPI, error) {
// It requires a token, provided by @BotFather on Telegram. // It requires a token, provided by @BotFather on Telegram.
func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) { func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
bot := &BotAPI{ bot := &BotAPI{
Token: token, Token: token,
Client: client, Client: client,
Buffer: 100, Buffer: 100,
shutdownChannel: make(chan interface{}), shutdownChannel: make(chan interface{}),
} }
@ -58,31 +59,11 @@ 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 Params) (APIResponse, error) { func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
if bot.Debug {
log.Printf("Endpoint: %s, params: %v\n", endpoint, params)
}
method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint) method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
values := buildParams(params) resp, err := bot.Client.PostForm(method, params)
resp, err := bot.Client.PostForm(method, values)
if err != nil { if err != nil {
return APIResponse{}, err return APIResponse{}, err
} }
@ -95,20 +76,15 @@ func (bot *BotAPI) MakeRequest(endpoint string, params Params) (APIResponse, err
} }
if bot.Debug { if bot.Debug {
log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes)) log.Printf("%s resp: %s", endpoint, bytes)
} }
if !apiResp.Ok { if !apiResp.Ok {
var parameters ResponseParameters parameters := ResponseParameters{}
if apiResp.Parameters != nil { if apiResp.Parameters != nil {
parameters = *apiResp.Parameters parameters = *apiResp.Parameters
} }
return apiResp, Error{apiResp.Description, parameters}
return apiResp, Error{
Message: apiResp.Description,
ResponseParameters: parameters,
}
} }
return apiResp, nil return apiResp, nil
@ -138,6 +114,21 @@ func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse)
return data, nil return data, nil
} }
// makeMessageRequest makes a request to a method that returns a Message.
func (bot *BotAPI) makeMessageRequest(endpoint string, params url.Values) (Message, error) {
resp, err := bot.MakeRequest(endpoint, params)
if err != nil {
return Message{}, err
}
var message Message
json.Unmarshal(resp.Result, &message)
bot.debugLog(endpoint, params, message)
return message, 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.
@ -146,7 +137,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 Params, fieldname string, file interface{}) (APIResponse, error) { func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
ms := multipartstreamer.New() ms := multipartstreamer.New()
switch f := file.(type) { switch f := file.(type) {
@ -195,10 +186,6 @@ func (bot *BotAPI) UploadFile(endpoint string, params Params, fieldname string,
return APIResponse{}, errors.New(ErrBadFileType) return APIResponse{}, errors.New(ErrBadFileType)
} }
if bot.Debug {
log.Printf("Endpoint: %s, fieldname: %s, params: %v, file: %T\n", endpoint, fieldname, params, file)
}
method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint) method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
req, err := http.NewRequest("POST", method, nil) req, err := http.NewRequest("POST", method, nil)
@ -220,7 +207,7 @@ func (bot *BotAPI) UploadFile(endpoint string, params Params, fieldname string,
} }
if bot.Debug { if bot.Debug {
log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes)) log.Println(string(bytes))
} }
var apiResp APIResponse var apiResp APIResponse
@ -262,9 +249,11 @@ func (bot *BotAPI) GetMe() (User, error) {
} }
var user User var user User
err = json.Unmarshal(resp.Result, &user) json.Unmarshal(resp.Result, &user)
bot.debugLog("getMe", nil, user)
return user, err return user, nil
} }
// IsMessageToMe returns true if message directed to this bot. // IsMessageToMe returns true if message directed to this bot.
@ -274,52 +263,90 @@ func (bot *BotAPI) IsMessageToMe(message Message) bool {
return strings.Contains(message.Text, "@"+bot.Self.UserName) return strings.Contains(message.Text, "@"+bot.Self.UserName)
} }
// Request sends a Chattable to Telegram, and returns the APIResponse. // Send will send a Chattable item to Telegram.
func (bot *BotAPI) Request(c Chattable) (APIResponse, error) { //
params, err := c.params() // It requires the Chattable to send.
if err != nil { func (bot *BotAPI) Send(c Chattable) (Message, error) {
return APIResponse{}, err switch c.(type) {
case Fileable:
return bot.sendFile(c.(Fileable))
default:
return bot.sendChattable(c)
}
}
// debugLog checks if the bot is currently running in debug mode, and if
// so will display information about the request and response in the
// debug log.
func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) {
if bot.Debug {
log.Printf("%s req : %+v\n", context, v)
log.Printf("%s resp: %+v\n", context, message)
} }
}
switch t := c.(type) { // sendExisting will send a Message with an existing file to Telegram.
case Fileable: func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) {
if t.useExistingFile() { v, err := config.values()
return bot.MakeRequest(t.method(), params)
}
return bot.UploadFile(t.method(), params, t.name(), t.getFile()) if err != nil {
default: return Message{}, err
return bot.MakeRequest(c.method(), params)
} }
message, err := bot.makeMessageRequest(method, v)
if err != nil {
return Message{}, err
}
return message, nil
} }
// Send will send a Chattable item to Telegram and provides the // uploadAndSend will send a Message with a new file to Telegram.
// returned Message. func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) {
func (bot *BotAPI) Send(c Chattable) (Message, error) { params, err := config.params()
resp, err := bot.Request(c) if err != nil {
return Message{}, err
}
file := config.getFile()
resp, err := bot.UploadFile(method, params, config.name(), file)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
var message Message var message Message
err = json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
bot.debugLog(method, nil, message)
return message, err return message, nil
} }
// SendMediaGroup sends a media group and returns the resulting messages. // sendFile determines if the file is using an existing file or uploading
func (bot *BotAPI) SendMediaGroup(config MediaGroupConfig) ([]Message, error) { // a new file, then sends it as needed.
params, _ := config.params() func (bot *BotAPI) sendFile(config Fileable) (Message, error) {
if config.useExistingFile() {
return bot.sendExisting(config.method(), config)
}
resp, err := bot.MakeRequest(config.method(), params) return bot.uploadAndSend(config.method(), config)
}
// sendChattable sends a Chattable.
func (bot *BotAPI) sendChattable(config Chattable) (Message, error) {
v, err := config.values()
if err != nil { if err != nil {
return nil, err return Message{}, err
} }
var messages []Message message, err := bot.makeMessageRequest(config.method(), v)
err = json.Unmarshal(resp.Result, &messages)
return messages, err if err != nil {
return Message{}, err
}
return message, nil
} }
// GetUserProfilePhotos gets a user's profile photos. // GetUserProfilePhotos gets a user's profile photos.
@ -327,34 +354,46 @@ func (bot *BotAPI) SendMediaGroup(config MediaGroupConfig) ([]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) {
params, _ := config.params() v := url.Values{}
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(config.method(), params) resp, err := bot.MakeRequest("getUserProfilePhotos", v)
if err != nil { if err != nil {
return UserProfilePhotos{}, err return UserProfilePhotos{}, err
} }
var profilePhotos UserProfilePhotos var profilePhotos UserProfilePhotos
err = json.Unmarshal(resp.Result, &profilePhotos) json.Unmarshal(resp.Result, &profilePhotos)
bot.debugLog("GetUserProfilePhoto", v, profilePhotos)
return profilePhotos, err return profilePhotos, nil
} }
// GetFile returns a File which can download a file from Telegram. // GetFile returns a File which can download a file from Telegram.
// //
// Requires FileID. // Requires FileID.
func (bot *BotAPI) GetFile(config FileConfig) (File, error) { func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
params, _ := config.params() v := url.Values{}
v.Add("file_id", config.FileID)
resp, err := bot.MakeRequest(config.method(), params) resp, err := bot.MakeRequest("getFile", v)
if err != nil { if err != nil {
return File{}, err return File{}, err
} }
var file File var file File
err = json.Unmarshal(resp.Result, &file) json.Unmarshal(resp.Result, &file)
return file, err bot.debugLog("GetFile", v, file)
return file, nil
} }
// GetUpdates fetches updates. // GetUpdates fetches updates.
@ -365,23 +404,71 @@ 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) {
params, _ := config.params() v := url.Values{}
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(config.method(), params) resp, err := bot.MakeRequest("getUpdates", v)
if err != nil { if err != nil {
return []Update{}, err return []Update{}, err
} }
var updates []Update var updates []Update
err = json.Unmarshal(resp.Result, &updates) json.Unmarshal(resp.Result, &updates)
bot.debugLog("getUpdates", v, updates)
return updates, nil
}
return updates, err // RemoveWebhook unsets the webhook.
func (bot *BotAPI) RemoveWebhook() (APIResponse, error) {
return bot.MakeRequest("setWebhook", url.Values{})
}
// SetWebhook sets a webhook.
//
// If this is set, GetUpdates will not get any data!
//
// If you do not have a legitimate TLS certificate, you need to include
// your self signed certificate with the config.
func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
if config.Certificate == nil {
v := url.Values{}
v.Add("url", config.URL.String())
if config.MaxConnections != 0 {
v.Add("max_connections", strconv.Itoa(config.MaxConnections))
}
return bot.MakeRequest("setWebhook", v)
}
params := make(map[string]string)
params["url"] = config.URL.String()
if config.MaxConnections != 0 {
params["max_connections"] = strconv.Itoa(config.MaxConnections)
}
resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate)
if err != nil {
return APIResponse{}, err
}
return resp, nil
} }
// 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", nil) resp, err := bot.MakeRequest("getWebhookInfo", url.Values{})
if err != nil { if err != nil {
return WebhookInfo{}, err return WebhookInfo{}, err
} }
@ -393,7 +480,7 @@ func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) {
} }
// GetUpdatesChan starts and returns a channel for getting updates. // GetUpdatesChan starts and returns a channel for getting updates.
func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) UpdatesChannel { func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
ch := make(chan Update, bot.Buffer) ch := make(chan Update, bot.Buffer)
go func() { go func() {
@ -422,7 +509,7 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) UpdatesChannel {
} }
}() }()
return ch return ch, nil
} }
// StopReceivingUpdates stops the go routine which receives updates // StopReceivingUpdates stops the go routine which receives updates
@ -449,11 +536,96 @@ func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
return ch return ch
} }
// AnswerInlineQuery sends a response to an inline query.
//
// Note that you must respond to an inline query within 30 seconds.
func (bot *BotAPI) AnswerInlineQuery(config InlineConfig) (APIResponse, error) {
v := url.Values{}
v.Add("inline_query_id", config.InlineQueryID)
v.Add("cache_time", strconv.Itoa(config.CacheTime))
v.Add("is_personal", strconv.FormatBool(config.IsPersonal))
v.Add("next_offset", config.NextOffset)
data, err := json.Marshal(config.Results)
if err != nil {
return APIResponse{}, err
}
v.Add("results", string(data))
v.Add("switch_pm_text", config.SwitchPMText)
v.Add("switch_pm_parameter", config.SwitchPMParameter)
bot.debugLog("answerInlineQuery", v, nil)
return bot.MakeRequest("answerInlineQuery", v)
}
// AnswerCallbackQuery sends a response to an inline query callback.
func (bot *BotAPI) AnswerCallbackQuery(config CallbackConfig) (APIResponse, error) {
v := url.Values{}
v.Add("callback_query_id", config.CallbackQueryID)
if config.Text != "" {
v.Add("text", config.Text)
}
v.Add("show_alert", strconv.FormatBool(config.ShowAlert))
if config.URL != "" {
v.Add("url", config.URL)
}
v.Add("cache_time", strconv.Itoa(config.CacheTime))
bot.debugLog("answerCallbackQuery", v, nil)
return bot.MakeRequest("answerCallbackQuery", v)
}
// KickChatMember kicks a user from a chat. Note that this only will work
// in supergroups, and requires the bot to be an admin. Also note they
// will be unable to rejoin until they are unbanned.
func (bot *BotAPI) KickChatMember(config KickChatMemberConfig) (APIResponse, error) {
v := url.Values{}
if config.SuperGroupUsername == "" {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
v.Add("user_id", strconv.Itoa(config.UserID))
if config.UntilDate != 0 {
v.Add("until_date", strconv.FormatInt(config.UntilDate, 10))
}
bot.debugLog("kickChatMember", v, nil)
return bot.MakeRequest("kickChatMember", v)
}
// LeaveChat makes the bot leave the chat.
func (bot *BotAPI) LeaveChat(config ChatConfig) (APIResponse, error) {
v := url.Values{}
if config.SuperGroupUsername == "" {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
bot.debugLog("leaveChat", v, nil)
return bot.MakeRequest("leaveChat", v)
}
// GetChat gets information about a chat. // GetChat gets information about a chat.
func (bot *BotAPI) GetChat(config ChatInfoConfig) (Chat, error) { func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) {
params, _ := config.params() v := url.Values{}
if config.SuperGroupUsername == "" {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
resp, err := bot.MakeRequest(config.method(), params) resp, err := bot.MakeRequest("getChat", v)
if err != nil { if err != nil {
return Chat{}, err return Chat{}, err
} }
@ -461,6 +633,8 @@ func (bot *BotAPI) GetChat(config ChatInfoConfig) (Chat, error) {
var chat Chat var chat Chat
err = json.Unmarshal(resp.Result, &chat) err = json.Unmarshal(resp.Result, &chat)
bot.debugLog("getChat", v, chat)
return chat, err return chat, err
} }
@ -468,10 +642,16 @@ func (bot *BotAPI) GetChat(config ChatInfoConfig) (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 ChatAdministratorsConfig) ([]ChatMember, error) { func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error) {
params, _ := config.params() v := url.Values{}
if config.SuperGroupUsername == "" {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
resp, err := bot.MakeRequest(config.method(), params) resp, err := bot.MakeRequest("getChatAdministrators", v)
if err != nil { if err != nil {
return []ChatMember{}, err return []ChatMember{}, err
} }
@ -479,14 +659,22 @@ func (bot *BotAPI) GetChatAdministrators(config ChatAdministratorsConfig) ([]Cha
var members []ChatMember var members []ChatMember
err = json.Unmarshal(resp.Result, &members) err = json.Unmarshal(resp.Result, &members)
bot.debugLog("getChatAdministrators", v, members)
return members, err return members, err
} }
// GetChatMembersCount gets the number of users in a chat. // GetChatMembersCount gets the number of users in a chat.
func (bot *BotAPI) GetChatMembersCount(config ChatMemberCountConfig) (int, error) { func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) {
params, _ := config.params() v := url.Values{}
resp, err := bot.MakeRequest(config.method(), params) if config.SuperGroupUsername == "" {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
resp, err := bot.MakeRequest("getChatMembersCount", v)
if err != nil { if err != nil {
return -1, err return -1, err
} }
@ -494,14 +682,23 @@ func (bot *BotAPI) GetChatMembersCount(config ChatMemberCountConfig) (int, error
var count int var count int
err = json.Unmarshal(resp.Result, &count) err = json.Unmarshal(resp.Result, &count)
bot.debugLog("getChatMembersCount", v, count)
return count, err return count, err
} }
// GetChatMember gets a specific chat member. // GetChatMember gets a specific chat member.
func (bot *BotAPI) GetChatMember(config GetChatMemberConfig) (ChatMember, error) { func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) {
params, _ := config.params() v := url.Values{}
resp, err := bot.MakeRequest(config.method(), params) if config.SuperGroupUsername == "" {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
v.Add("user_id", strconv.Itoa(config.UserID))
resp, err := bot.MakeRequest("getChatMember", v)
if err != nil { if err != nil {
return ChatMember{}, err return ChatMember{}, err
} }
@ -509,14 +706,115 @@ func (bot *BotAPI) GetChatMember(config GetChatMemberConfig) (ChatMember, error)
var member ChatMember var member ChatMember
err = json.Unmarshal(resp.Result, &member) err = json.Unmarshal(resp.Result, &member)
bot.debugLog("getChatMember", v, member)
return member, err return member, err
} }
// 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.
func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error) {
v := url.Values{}
if config.SuperGroupUsername != "" {
v.Add("chat_id", config.SuperGroupUsername)
} 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))
bot.debugLog("unbanChatMember", v, nil)
return bot.MakeRequest("unbanChatMember", v)
}
// RestrictChatMember to restrict a user in a supergroup. The bot must be an
//administrator in the supergroup for this to work and must have the
//appropriate admin rights. Pass True for all boolean parameters to lift
//restrictions from a user. Returns True on success.
func (bot *BotAPI) RestrictChatMember(config RestrictChatMemberConfig) (APIResponse, error) {
v := url.Values{}
if config.SuperGroupUsername != "" {
v.Add("chat_id", config.SuperGroupUsername)
} 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.Add("can_send_messages", strconv.FormatBool(*config.CanSendMessages))
}
if config.CanSendMediaMessages != nil {
v.Add("can_send_media_messages", strconv.FormatBool(*config.CanSendMediaMessages))
}
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))
}
bot.debugLog("restrictChatMember", v, nil)
return bot.MakeRequest("restrictChatMember", v)
}
// PromoteChatMember add admin rights to user
func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) {
v := url.Values{}
if config.SuperGroupUsername != "" {
v.Add("chat_id", config.SuperGroupUsername)
} 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.Add("can_change_info", strconv.FormatBool(*config.CanChangeInfo))
}
if config.CanPostMessages != nil {
v.Add("can_post_messages", strconv.FormatBool(*config.CanPostMessages))
}
if config.CanEditMessages != nil {
v.Add("can_edit_messages", strconv.FormatBool(*config.CanEditMessages))
}
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))
}
bot.debugLog("promoteChatMember", v, nil)
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) {
params, _ := config.params() v, _ := config.values()
resp, err := bot.MakeRequest(config.method(), params) resp, err := bot.MakeRequest(config.method(), v)
if err != nil { if err != nil {
return []GameHighScore{}, err return []GameHighScore{}, err
} }
@ -527,11 +825,65 @@ func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHigh
return highScores, err return highScores, err
} }
// AnswerShippingQuery allows you to reply to Update with shipping_query parameter.
func (bot *BotAPI) AnswerShippingQuery(config ShippingConfig) (APIResponse, error) {
v := url.Values{}
v.Add("shipping_query_id", config.ShippingQueryID)
v.Add("ok", strconv.FormatBool(config.OK))
if config.OK == true {
data, err := json.Marshal(config.ShippingOptions)
if err != nil {
return APIResponse{}, err
}
v.Add("shipping_options", string(data))
} else {
v.Add("error_message", config.ErrorMessage)
}
bot.debugLog("answerShippingQuery", v, nil)
return bot.MakeRequest("answerShippingQuery", v)
}
// AnswerPreCheckoutQuery allows you to reply to Update with pre_checkout_query.
func (bot *BotAPI) AnswerPreCheckoutQuery(config PreCheckoutConfig) (APIResponse, error) {
v := url.Values{}
v.Add("pre_checkout_query_id", config.PreCheckoutQueryID)
v.Add("ok", strconv.FormatBool(config.OK))
if config.OK != true {
v.Add("error", config.ErrorMessage)
}
bot.debugLog("answerPreCheckoutQuery", v, nil)
return bot.MakeRequest("answerPreCheckoutQuery", v)
}
// DeleteMessage deletes a message in a chat
func (bot *BotAPI) DeleteMessage(config DeleteMessageConfig) (APIResponse, error) {
v, err := config.values()
if err != nil {
return APIResponse{}, err
}
bot.debugLog(config.method(), v, nil)
return bot.MakeRequest(config.method(), v)
}
// GetInviteLink get InviteLink for a chat // GetInviteLink get InviteLink for a chat
func (bot *BotAPI) GetInviteLink(config ChatInviteLinkConfig) (string, error) { func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) {
params, _ := config.params() v := url.Values{}
if config.SuperGroupUsername == "" {
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
} else {
v.Add("chat_id", config.SuperGroupUsername)
}
resp, err := bot.MakeRequest(config.method(), params) resp, err := bot.MakeRequest("exportChatInviteLink", v)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -542,35 +894,74 @@ func (bot *BotAPI) GetInviteLink(config ChatInviteLinkConfig) (string, error) {
return inviteLink, err return inviteLink, err
} }
// GetStickerSet returns a StickerSet. // PinChatMessage pin message in supergroup
func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) { func (bot *BotAPI) PinChatMessage(config PinChatMessageConfig) (APIResponse, error) {
params, _ := config.params() v, err := config.values()
if err != nil {
return APIResponse{}, err
}
bot.debugLog(config.method(), v, nil)
return bot.MakeRequest(config.method(), v)
}
// UnpinChatMessage unpin message in supergroup
func (bot *BotAPI) UnpinChatMessage(config UnpinChatMessageConfig) (APIResponse, error) {
v, err := config.values()
if err != nil {
return APIResponse{}, err
}
bot.debugLog(config.method(), v, nil)
return bot.MakeRequest(config.method(), v)
}
resp, err := bot.MakeRequest(config.method(), params) // SetChatTitle change title of chat.
func (bot *BotAPI) SetChatTitle(config SetChatTitleConfig) (APIResponse, error) {
v, err := config.values()
if err != nil { if err != nil {
return StickerSet{}, err return APIResponse{}, err
} }
var stickers StickerSet bot.debugLog(config.method(), v, nil)
err = json.Unmarshal(resp.Result, &stickers)
return stickers, err return bot.MakeRequest(config.method(), v)
} }
// StopPoll stops a poll and returns the result. // SetChatDescription change description of chat.
func (bot *BotAPI) StopPoll(config StopPollConfig) (Poll, error) { func (bot *BotAPI) SetChatDescription(config SetChatDescriptionConfig) (APIResponse, error) {
v, err := config.values()
if err != nil {
return APIResponse{}, err
}
bot.debugLog(config.method(), v, nil)
return bot.MakeRequest(config.method(), v)
}
// SetChatPhoto change photo of chat.
func (bot *BotAPI) SetChatPhoto(config SetChatPhotoConfig) (APIResponse, error) {
params, err := config.params() params, err := config.params()
if err != nil { if err != nil {
return Poll{}, err return APIResponse{}, err
} }
resp, err := bot.MakeRequest(config.method(), params) 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 { if err != nil {
return Poll{}, err return APIResponse{}, err
} }
var poll Poll bot.debugLog(config.method(), v, nil)
err = json.Unmarshal(resp.Result, &poll)
return poll, err return bot.MakeRequest(config.method(), v)
} }

@ -1,11 +1,14 @@
package tgbotapi package tgbotapi_test
import ( import (
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"os" "os"
"testing" "testing"
"time" "time"
"git.tomans.ru/Tomansru/telegram-bot-api"
) )
const ( const (
@ -22,8 +25,8 @@ const (
ExistingStickerFileID = "BQADAgADcwADjMcoCbdl-6eB--YPAg" ExistingStickerFileID = "BQADAgADcwADjMcoCbdl-6eB--YPAg"
) )
func getBot(t *testing.T) (*BotAPI, error) { func getBot(t *testing.T) (*tgbotapi.BotAPI, error) {
bot, err := NewBotAPI(TestToken) bot, err := tgbotapi.NewBotAPI(TestToken)
bot.Debug = true bot.Debug = true
if err != nil { if err != nil {
@ -35,7 +38,7 @@ func getBot(t *testing.T) (*BotAPI, error) {
} }
func TestNewBotAPI_notoken(t *testing.T) { func TestNewBotAPI_notoken(t *testing.T) {
_, err := NewBotAPI("") _, err := tgbotapi.NewBotAPI("")
if err == nil { if err == nil {
t.Error(err) t.Error(err)
@ -46,7 +49,7 @@ func TestNewBotAPI_notoken(t *testing.T) {
func TestGetUpdates(t *testing.T) { func TestGetUpdates(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
u := NewUpdate(0) u := tgbotapi.NewUpdate(0)
_, err := bot.GetUpdates(u) _, err := bot.GetUpdates(u)
@ -59,7 +62,7 @@ func TestGetUpdates(t *testing.T) {
func TestSendWithMessage(t *testing.T) { func TestSendWithMessage(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewMessage(ChatID, "A test message from the test library in telegram-bot-api") msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api")
msg.ParseMode = "markdown" msg.ParseMode = "markdown"
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -72,7 +75,7 @@ func TestSendWithMessage(t *testing.T) {
func TestSendWithMessageReply(t *testing.T) { func TestSendWithMessageReply(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewMessage(ChatID, "A test message from the test library in telegram-bot-api") msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api")
msg.ReplyToMessageID = ReplyToMessageID msg.ReplyToMessageID = ReplyToMessageID
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -85,7 +88,7 @@ func TestSendWithMessageReply(t *testing.T) {
func TestSendWithMessageForward(t *testing.T) { func TestSendWithMessageForward(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewForward(ChatID, ChatID, ReplyToMessageID) msg := tgbotapi.NewForward(ChatID, ChatID, ReplyToMessageID)
_, err := bot.Send(msg) _, err := bot.Send(msg)
if err != nil { if err != nil {
@ -97,7 +100,7 @@ func TestSendWithMessageForward(t *testing.T) {
func TestSendWithNewPhoto(t *testing.T) { func TestSendWithNewPhoto(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewPhotoUpload(ChatID, "tests/image.jpg") msg := tgbotapi.NewPhotoUpload(ChatID, "tests/image.jpg")
msg.Caption = "Test" msg.Caption = "Test"
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -111,9 +114,9 @@ func TestSendWithNewPhotoWithFileBytes(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
data, _ := ioutil.ReadFile("tests/image.jpg") data, _ := ioutil.ReadFile("tests/image.jpg")
b := FileBytes{Name: "image.jpg", Bytes: data} b := tgbotapi.FileBytes{Name: "image.jpg", Bytes: data}
msg := NewPhotoUpload(ChatID, b) msg := tgbotapi.NewPhotoUpload(ChatID, b)
msg.Caption = "Test" msg.Caption = "Test"
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -127,9 +130,9 @@ func TestSendWithNewPhotoWithFileReader(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
f, _ := os.Open("tests/image.jpg") f, _ := os.Open("tests/image.jpg")
reader := FileReader{Name: "image.jpg", Reader: f, Size: -1} reader := tgbotapi.FileReader{Name: "image.jpg", Reader: f, Size: -1}
msg := NewPhotoUpload(ChatID, reader) msg := tgbotapi.NewPhotoUpload(ChatID, reader)
msg.Caption = "Test" msg.Caption = "Test"
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -142,7 +145,7 @@ func TestSendWithNewPhotoWithFileReader(t *testing.T) {
func TestSendWithNewPhotoReply(t *testing.T) { func TestSendWithNewPhotoReply(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewPhotoUpload(ChatID, "tests/image.jpg") msg := tgbotapi.NewPhotoUpload(ChatID, "tests/image.jpg")
msg.ReplyToMessageID = ReplyToMessageID msg.ReplyToMessageID = ReplyToMessageID
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -156,7 +159,7 @@ func TestSendWithNewPhotoReply(t *testing.T) {
func TestSendWithExistingPhoto(t *testing.T) { func TestSendWithExistingPhoto(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewPhotoShare(ChatID, ExistingPhotoFileID) msg := tgbotapi.NewPhotoShare(ChatID, ExistingPhotoFileID)
msg.Caption = "Test" msg.Caption = "Test"
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -169,7 +172,7 @@ func TestSendWithExistingPhoto(t *testing.T) {
func TestSendWithNewDocument(t *testing.T) { func TestSendWithNewDocument(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewDocumentUpload(ChatID, "tests/image.jpg") msg := tgbotapi.NewDocumentUpload(ChatID, "tests/image.jpg")
_, err := bot.Send(msg) _, err := bot.Send(msg)
if err != nil { if err != nil {
@ -181,7 +184,7 @@ func TestSendWithNewDocument(t *testing.T) {
func TestSendWithExistingDocument(t *testing.T) { func TestSendWithExistingDocument(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewDocumentShare(ChatID, ExistingDocumentFileID) msg := tgbotapi.NewDocumentShare(ChatID, ExistingDocumentFileID)
_, err := bot.Send(msg) _, err := bot.Send(msg)
if err != nil { if err != nil {
@ -193,7 +196,7 @@ func TestSendWithExistingDocument(t *testing.T) {
func TestSendWithNewAudio(t *testing.T) { func TestSendWithNewAudio(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewAudioUpload(ChatID, "tests/audio.mp3") msg := tgbotapi.NewAudioUpload(ChatID, "tests/audio.mp3")
msg.Title = "TEST" msg.Title = "TEST"
msg.Duration = 10 msg.Duration = 10
msg.Performer = "TEST" msg.Performer = "TEST"
@ -210,7 +213,7 @@ func TestSendWithNewAudio(t *testing.T) {
func TestSendWithExistingAudio(t *testing.T) { func TestSendWithExistingAudio(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewAudioShare(ChatID, ExistingAudioFileID) msg := tgbotapi.NewAudioShare(ChatID, ExistingAudioFileID)
msg.Title = "TEST" msg.Title = "TEST"
msg.Duration = 10 msg.Duration = 10
msg.Performer = "TEST" msg.Performer = "TEST"
@ -226,7 +229,7 @@ func TestSendWithExistingAudio(t *testing.T) {
func TestSendWithNewVoice(t *testing.T) { func TestSendWithNewVoice(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewVoiceUpload(ChatID, "tests/voice.ogg") msg := tgbotapi.NewVoiceUpload(ChatID, "tests/voice.ogg")
msg.Duration = 10 msg.Duration = 10
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -239,7 +242,7 @@ func TestSendWithNewVoice(t *testing.T) {
func TestSendWithExistingVoice(t *testing.T) { func TestSendWithExistingVoice(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewVoiceShare(ChatID, ExistingVoiceFileID) msg := tgbotapi.NewVoiceShare(ChatID, ExistingVoiceFileID)
msg.Duration = 10 msg.Duration = 10
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -252,7 +255,7 @@ func TestSendWithExistingVoice(t *testing.T) {
func TestSendWithContact(t *testing.T) { func TestSendWithContact(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
contact := NewContact(ChatID, "5551234567", "Test") contact := tgbotapi.NewContact(ChatID, "5551234567", "Test")
if _, err := bot.Send(contact); err != nil { if _, err := bot.Send(contact); err != nil {
t.Error(err) t.Error(err)
@ -263,7 +266,7 @@ func TestSendWithContact(t *testing.T) {
func TestSendWithLocation(t *testing.T) { func TestSendWithLocation(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
_, err := bot.Send(NewLocation(ChatID, 40, 40)) _, err := bot.Send(tgbotapi.NewLocation(ChatID, 40, 40))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -274,7 +277,7 @@ func TestSendWithLocation(t *testing.T) {
func TestSendWithVenue(t *testing.T) { func TestSendWithVenue(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
venue := NewVenue(ChatID, "A Test Location", "123 Test Street", 40, 40) venue := tgbotapi.NewVenue(ChatID, "A Test Location", "123 Test Street", 40, 40)
if _, err := bot.Send(venue); err != nil { if _, err := bot.Send(venue); err != nil {
t.Error(err) t.Error(err)
@ -285,7 +288,7 @@ func TestSendWithVenue(t *testing.T) {
func TestSendWithNewVideo(t *testing.T) { func TestSendWithNewVideo(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewVideoUpload(ChatID, "tests/video.mp4") msg := tgbotapi.NewVideoUpload(ChatID, "tests/video.mp4")
msg.Duration = 10 msg.Duration = 10
msg.Caption = "TEST" msg.Caption = "TEST"
@ -300,7 +303,7 @@ func TestSendWithNewVideo(t *testing.T) {
func TestSendWithExistingVideo(t *testing.T) { func TestSendWithExistingVideo(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewVideoShare(ChatID, ExistingVideoFileID) msg := tgbotapi.NewVideoShare(ChatID, ExistingVideoFileID)
msg.Duration = 10 msg.Duration = 10
msg.Caption = "TEST" msg.Caption = "TEST"
@ -315,7 +318,7 @@ func TestSendWithExistingVideo(t *testing.T) {
func TestSendWithNewVideoNote(t *testing.T) { func TestSendWithNewVideoNote(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewVideoNoteUpload(ChatID, 240, "tests/videonote.mp4") msg := tgbotapi.NewVideoNoteUpload(ChatID, 240, "tests/videonote.mp4")
msg.Duration = 10 msg.Duration = 10
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -329,7 +332,7 @@ func TestSendWithNewVideoNote(t *testing.T) {
func TestSendWithExistingVideoNote(t *testing.T) { func TestSendWithExistingVideoNote(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewVideoNoteShare(ChatID, 240, ExistingVideoNoteFileID) msg := tgbotapi.NewVideoNoteShare(ChatID, 240, ExistingVideoNoteFileID)
msg.Duration = 10 msg.Duration = 10
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -343,7 +346,7 @@ func TestSendWithExistingVideoNote(t *testing.T) {
func TestSendWithNewSticker(t *testing.T) { func TestSendWithNewSticker(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewStickerUpload(ChatID, "tests/image.jpg") msg := tgbotapi.NewStickerUpload(ChatID, "tests/image.jpg")
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -356,7 +359,7 @@ func TestSendWithNewSticker(t *testing.T) {
func TestSendWithExistingSticker(t *testing.T) { func TestSendWithExistingSticker(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewStickerShare(ChatID, ExistingStickerFileID) msg := tgbotapi.NewStickerShare(ChatID, ExistingStickerFileID)
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -369,8 +372,8 @@ func TestSendWithExistingSticker(t *testing.T) {
func TestSendWithNewStickerAndKeyboardHide(t *testing.T) { func TestSendWithNewStickerAndKeyboardHide(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewStickerUpload(ChatID, "tests/image.jpg") msg := tgbotapi.NewStickerUpload(ChatID, "tests/image.jpg")
msg.ReplyMarkup = ReplyKeyboardRemove{ msg.ReplyMarkup = tgbotapi.ReplyKeyboardRemove{
RemoveKeyboard: true, RemoveKeyboard: true,
Selective: false, Selective: false,
} }
@ -385,8 +388,8 @@ func TestSendWithNewStickerAndKeyboardHide(t *testing.T) {
func TestSendWithExistingStickerAndKeyboardHide(t *testing.T) { func TestSendWithExistingStickerAndKeyboardHide(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewStickerShare(ChatID, ExistingStickerFileID) msg := tgbotapi.NewStickerShare(ChatID, ExistingStickerFileID)
msg.ReplyMarkup = ReplyKeyboardRemove{ msg.ReplyMarkup = tgbotapi.ReplyKeyboardRemove{
RemoveKeyboard: true, RemoveKeyboard: true,
Selective: false, Selective: false,
} }
@ -402,9 +405,7 @@ func TestSendWithExistingStickerAndKeyboardHide(t *testing.T) {
func TestGetFile(t *testing.T) { func TestGetFile(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
file := FileConfig{ file := tgbotapi.FileConfig{FileID: ExistingPhotoFileID}
FileID: ExistingPhotoFileID,
}
_, err := bot.GetFile(file) _, err := bot.GetFile(file)
@ -417,7 +418,7 @@ func TestGetFile(t *testing.T) {
func TestSendChatConfig(t *testing.T) { func TestSendChatConfig(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
_, err := bot.Request(NewChatAction(ChatID, ChatTyping)) _, err := bot.Send(tgbotapi.NewChatAction(ChatID, tgbotapi.ChatTyping))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -428,14 +429,14 @@ func TestSendChatConfig(t *testing.T) {
func TestSendEditMessage(t *testing.T) { func TestSendEditMessage(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg, err := bot.Send(NewMessage(ChatID, "Testing editing.")) msg, err := bot.Send(tgbotapi.NewMessage(ChatID, "Testing editing."))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
t.Fail() t.Fail()
} }
edit := EditMessageTextConfig{ edit := tgbotapi.EditMessageTextConfig{
BaseEdit: BaseEdit{ BaseEdit: tgbotapi.BaseEdit{
ChatID: ChatID, ChatID: ChatID,
MessageID: msg.MessageID, MessageID: msg.MessageID,
}, },
@ -452,7 +453,7 @@ func TestSendEditMessage(t *testing.T) {
func TestGetUserProfilePhotos(t *testing.T) { func TestGetUserProfilePhotos(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
_, err := bot.GetUserProfilePhotos(NewUserProfilePhotos(ChatID)) _, err := bot.GetUserProfilePhotos(tgbotapi.NewUserProfilePhotos(ChatID))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
t.Fail() t.Fail()
@ -464,22 +465,19 @@ func TestSetWebhookWithCert(t *testing.T) {
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
bot.Request(RemoveWebhookConfig{}) bot.RemoveWebhook()
wh := NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, "tests/cert.pem") wh := tgbotapi.NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, "tests/cert.pem")
_, err := bot.Request(wh) _, err := bot.SetWebhook(wh)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
t.Fail() t.Fail()
} }
_, err = bot.GetWebhookInfo() _, err = bot.GetWebhookInfo()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
bot.RemoveWebhook()
bot.Request(RemoveWebhookConfig{})
} }
func TestSetWebhookWithoutCert(t *testing.T) { func TestSetWebhookWithoutCert(t *testing.T) {
@ -487,65 +485,65 @@ func TestSetWebhookWithoutCert(t *testing.T) {
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
bot.Request(RemoveWebhookConfig{}) bot.RemoveWebhook()
wh := NewWebhook("https://example.com/tgbotapi-test/" + bot.Token) wh := tgbotapi.NewWebhook("https://example.com/tgbotapi-test/" + bot.Token)
_, err := bot.Request(wh) _, err := bot.SetWebhook(wh)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
t.Fail() t.Fail()
} }
info, err := bot.GetWebhookInfo() info, err := bot.GetWebhookInfo()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if info.LastErrorDate != 0 { if info.LastErrorDate != 0 {
t.Errorf("failed to set webhook: %s", info.LastErrorMessage) t.Errorf("[Telegram callback failed]%s", info.LastErrorMessage)
} }
bot.RemoveWebhook()
bot.Request(RemoveWebhookConfig{})
} }
func TestSendWithMediaGroup(t *testing.T) { func TestUpdatesChan(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
cfg := NewMediaGroup(ChatID, []interface{}{ var ucfg tgbotapi.UpdateConfig = tgbotapi.NewUpdate(0)
NewInputMediaPhoto("https://i.imgur.com/unQLJIb.jpg"), ucfg.Timeout = 60
NewInputMediaPhoto("https://i.imgur.com/J5qweNZ.jpg"), _, err := bot.GetUpdatesChan(ucfg)
NewInputMediaVideo("https://i.imgur.com/F6RmI24.mp4"),
})
messages, err := bot.SendMediaGroup(cfg)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
t.Fail()
} }
}
if messages == nil { func TestSendWithMediaGroup(t *testing.T) {
t.Error() bot, _ := getBot(t)
}
if len(messages) != 3 { cfg := tgbotapi.NewMediaGroup(ChatID, []interface{}{
t.Error() tgbotapi.NewInputMediaPhoto("https://i.imgur.com/unQLJIb.jpg"),
tgbotapi.NewInputMediaPhoto("https://i.imgur.com/J5qweNZ.jpg"),
tgbotapi.NewInputMediaVideo("https://i.imgur.com/F6RmI24.mp4"),
})
_, err := bot.Send(cfg)
if err != nil {
t.Error(err)
} }
} }
func ExampleNewBotAPI() { func ExampleNewBotAPI() {
bot, err := NewBotAPI("MyAwesomeBotToken") bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken")
if err != nil { if err != nil {
panic(err) log.Panic(err)
} }
bot.Debug = true bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName) log.Printf("Authorized on account %s", bot.Self.UserName)
u := NewUpdate(0) u := tgbotapi.NewUpdate(0)
u.Timeout = 60 u.Timeout = 60
updates := bot.GetUpdatesChan(u) updates, err := bot.GetUpdatesChan(u)
// Optional: wait for updates and clear them if you don't want to handle // Optional: wait for updates and clear them if you don't want to handle
// a large backlog of old messages // a large backlog of old messages
@ -559,7 +557,7 @@ func ExampleNewBotAPI() {
log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text) log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text)
msg := NewMessage(update.Message.Chat.ID, update.Message.Text) msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text)
msg.ReplyToMessageID = update.Message.MessageID msg.ReplyToMessageID = update.Message.MessageID
bot.Send(msg) bot.Send(msg)
@ -567,30 +565,26 @@ func ExampleNewBotAPI() {
} }
func ExampleNewWebhook() { func ExampleNewWebhook() {
bot, err := NewBotAPI("MyAwesomeBotToken") bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken")
if err != nil { if err != nil {
panic(err) log.Fatal(err)
} }
bot.Debug = true bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName) log.Printf("Authorized on account %s", bot.Self.UserName)
_, err = bot.Request(NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem")) _, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem"))
if err != nil { if err != nil {
panic(err) log.Fatal(err)
} }
info, err := bot.GetWebhookInfo() info, err := bot.GetWebhookInfo()
if err != nil { if err != nil {
panic(err) log.Fatal(err)
} }
if info.LastErrorDate != 0 { if info.LastErrorDate != 0 {
log.Printf("failed to set webhook: %s", info.LastErrorMessage) log.Printf("[Telegram callback failed]%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)
@ -599,35 +593,35 @@ func ExampleNewWebhook() {
} }
} }
func ExampleInlineConfig() { func ExampleAnswerInlineQuery() {
bot, err := NewBotAPI("MyAwesomeBotToken") // create new bot bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") // create new bot
if err != nil { if err != nil {
panic(err) log.Panic(err)
} }
log.Printf("Authorized on account %s", bot.Self.UserName) log.Printf("Authorized on account %s", bot.Self.UserName)
u := NewUpdate(0) u := tgbotapi.NewUpdate(0)
u.Timeout = 60 u.Timeout = 60
updates := bot.GetUpdatesChan(u) updates, err := bot.GetUpdatesChan(u)
for update := range updates { for update := range updates {
if update.InlineQuery == nil { // if no inline query, ignore it if update.InlineQuery == nil { // if no inline query, ignore it
continue continue
} }
article := NewInlineQueryResultArticle(update.InlineQuery.ID, "Echo", update.InlineQuery.Query) article := tgbotapi.NewInlineQueryResultArticle(update.InlineQuery.ID, "Echo", update.InlineQuery.Query)
article.Description = update.InlineQuery.Query article.Description = update.InlineQuery.Query
inlineConf := InlineConfig{ inlineConf := tgbotapi.InlineConfig{
InlineQueryID: update.InlineQuery.ID, InlineQueryID: update.InlineQuery.ID,
IsPersonal: true, IsPersonal: true,
CacheTime: 0, CacheTime: 0,
Results: []interface{}{article}, Results: []interface{}{article},
} }
if _, err := bot.Request(inlineConf); err != nil { if _, err := bot.AnswerInlineQuery(inlineConf); err != nil {
log.Println(err) log.Println(err)
} }
} }
@ -636,15 +630,15 @@ func ExampleInlineConfig() {
func TestDeleteMessage(t *testing.T) { func TestDeleteMessage(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewMessage(ChatID, "A test message from the test library in telegram-bot-api") msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api")
msg.ParseMode = "markdown" msg.ParseMode = "markdown"
message, _ := bot.Send(msg) message, _ := bot.Send(msg)
deleteMessageConfig := DeleteMessageConfig{ deleteMessageConfig := tgbotapi.DeleteMessageConfig{
ChatID: message.Chat.ID, ChatID: message.Chat.ID,
MessageID: message.MessageID, MessageID: message.MessageID,
} }
_, err := bot.Request(deleteMessageConfig) _, err := bot.DeleteMessage(deleteMessageConfig)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -655,16 +649,16 @@ func TestDeleteMessage(t *testing.T) {
func TestPinChatMessage(t *testing.T) { func TestPinChatMessage(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api") msg := tgbotapi.NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api")
msg.ParseMode = "markdown" msg.ParseMode = "markdown"
message, _ := bot.Send(msg) message, _ := bot.Send(msg)
pinChatMessageConfig := PinChatMessageConfig{ pinChatMessageConfig := tgbotapi.PinChatMessageConfig{
ChatID: message.Chat.ID, ChatID: message.Chat.ID,
MessageID: message.MessageID, MessageID: message.MessageID,
DisableNotification: false, DisableNotification: false,
} }
_, err := bot.Request(pinChatMessageConfig) _, err := bot.PinChatMessage(pinChatMessageConfig)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -675,61 +669,25 @@ func TestPinChatMessage(t *testing.T) {
func TestUnpinChatMessage(t *testing.T) { func TestUnpinChatMessage(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api") msg := tgbotapi.NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api")
msg.ParseMode = "markdown" msg.ParseMode = "markdown"
message, _ := bot.Send(msg) message, _ := bot.Send(msg)
// We need pin message to unpin something // We need pin message to unpin something
pinChatMessageConfig := PinChatMessageConfig{ pinChatMessageConfig := tgbotapi.PinChatMessageConfig{
ChatID: message.Chat.ID, ChatID: message.Chat.ID,
MessageID: message.MessageID, MessageID: message.MessageID,
DisableNotification: false, DisableNotification: false,
} }
_, err := bot.PinChatMessage(pinChatMessageConfig)
if _, err := bot.Request(pinChatMessageConfig); err != nil { unpinChatMessageConfig := tgbotapi.UnpinChatMessageConfig{
t.Error(err)
t.Fail()
}
unpinChatMessageConfig := UnpinChatMessageConfig{
ChatID: message.Chat.ID, ChatID: message.Chat.ID,
} }
_, err = bot.UnpinChatMessage(unpinChatMessageConfig)
if _, err := bot.Request(unpinChatMessageConfig); err != nil {
t.Error(err)
t.Fail()
}
}
func TestPolls(t *testing.T) {
bot, _ := getBot(t)
poll := NewPoll(SupergroupChatID, "Are polls working?", "Yes", "No")
msg, err := bot.Send(poll)
if err != nil {
t.Error(err)
t.Fail()
}
result, err := bot.StopPoll(NewStopPoll(SupergroupChatID, msg.MessageID))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
t.Fail() t.Fail()
} }
if result.Question != "Are polls working?" {
t.Error("Poll question did not match")
t.Fail()
}
if !result.IsClosed {
t.Error("Poll did not end")
t.Fail()
}
if result.Options[0].Text != "Yes" || result.Options[0].VoterCount != 0 || result.Options[1].Text != "No" || result.Options[1].VoterCount != 0 {
t.Error("Poll options were incorrect")
t.Fail()
}
} }

File diff suppressed because it is too large Load Diff

@ -1,3 +1,5 @@
module github.com/go-telegram-bot-api/telegram-bot-api/v5 module git.tomans.ru/Tomansru/telegram-bot-api
go 1.13
require github.com/technoweenie/multipartstreamer v1.0.1 require github.com/technoweenie/multipartstreamer v1.0.1

@ -294,58 +294,26 @@ func NewVoiceShare(chatID int64, fileID string) VoiceConfig {
// two to ten InputMediaPhoto or InputMediaVideo. // two to ten InputMediaPhoto or InputMediaVideo.
func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig { func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig {
return MediaGroupConfig{ return MediaGroupConfig{
ChatID: chatID, BaseChat: BaseChat{
Media: files, ChatID: chatID,
},
InputMedia: files,
} }
} }
// NewInputMediaPhoto creates a new InputMediaPhoto. // NewInputMediaPhoto creates a new InputMediaPhoto.
func NewInputMediaPhoto(media string) InputMediaPhoto { func NewInputMediaPhoto(media string) InputMediaPhoto {
return InputMediaPhoto{ return InputMediaPhoto{
BaseInputMedia{ Type: "photo",
Type: "photo", Media: media,
Media: media,
},
} }
} }
// NewInputMediaVideo creates a new InputMediaVideo. // NewInputMediaVideo creates a new InputMediaVideo.
func NewInputMediaVideo(media string) InputMediaVideo { func NewInputMediaVideo(media string) InputMediaVideo {
return InputMediaVideo{ return InputMediaVideo{
BaseInputMedia: BaseInputMedia{ Type: "video",
Type: "video", Media: media,
Media: media,
},
}
}
// NewInputMediaAnimation creates a new InputMediaAnimation.
func NewInputMediaAnimation(media string) InputMediaAnimation {
return InputMediaAnimation{
BaseInputMedia: BaseInputMedia{
Type: "animation",
Media: media,
},
}
}
// NewInputMediaAudio creates a new InputMediaAudio.
func NewInputMediaAudio(media string) InputMediaAudio {
return InputMediaAudio{
BaseInputMedia: BaseInputMedia{
Type: "audio",
Media: media,
},
}
}
// NewInputMediaDocument creates a new InputMediaDocument.
func NewInputMediaDocument(media string) InputMediaDocument {
return InputMediaDocument{
BaseInputMedia: BaseInputMedia{
Type: "document",
Media: media,
},
} }
} }
@ -737,7 +705,7 @@ func NewCallbackWithAlert(id, text string) CallbackConfig {
} }
// NewInvoice creates 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},
Title: title, Title: title,
@ -779,59 +747,3 @@ func NewSetChatPhotoShare(chatID int64, fileID string) SetChatPhotoConfig {
}, },
} }
} }
// NewChatTitle allows you to update the title of a chat.
func NewChatTitle(chatID int64, title string) SetChatTitleConfig {
return SetChatTitleConfig{
ChatID: chatID,
Title: title,
}
}
// NewChatDescription allows you to update the description of a chat.
func NewChatDescription(chatID int64, description string) SetChatDescriptionConfig {
return SetChatDescriptionConfig{
ChatID: chatID,
Description: description,
}
}
// NewChatPhoto allows you to update the photo for a chat.
func NewChatPhoto(chatID int64, photo interface{}) SetChatPhotoConfig {
return SetChatPhotoConfig{
BaseFile: BaseFile{
BaseChat: BaseChat{
ChatID: chatID,
},
File: photo,
},
}
}
// NewDeleteChatPhoto allows you to delete the photo for a chat.
func NewDeleteChatPhoto(chatID int64, photo interface{}) DeleteChatPhotoConfig {
return DeleteChatPhotoConfig{
ChatID: chatID,
}
}
// NewPoll allows you to create a new poll.
func NewPoll(chatID int64, question string, options ...string) SendPollConfig {
return SendPollConfig{
BaseChat: BaseChat{
ChatID: chatID,
},
Question: question,
Options: options,
}
}
// NewStopPoll allows you to stop a poll.
func NewStopPoll(chatID int64, messageID int) StopPollConfig {
return StopPollConfig{
BaseEdit{
ChatID: chatID,
MessageID: messageID,
},
}
}

@ -1,46 +1,48 @@
package tgbotapi package tgbotapi_test
import ( import (
"testing" "testing"
"git.tomans.ru/Tomansru/telegram-bot-api"
) )
func TestNewInlineQueryResultArticle(t *testing.T) { func TestNewInlineQueryResultArticle(t *testing.T) {
result := NewInlineQueryResultArticle("id", "title", "message") result := tgbotapi.NewInlineQueryResultArticle("id", "title", "message")
if result.Type != "article" || if result.Type != "article" ||
result.ID != "id" || result.ID != "id" ||
result.Title != "title" || result.Title != "title" ||
result.InputMessageContent.(InputTextMessageContent).Text != "message" { result.InputMessageContent.(tgbotapi.InputTextMessageContent).Text != "message" {
t.Fail() t.Fail()
} }
} }
func TestNewInlineQueryResultArticleMarkdown(t *testing.T) { func TestNewInlineQueryResultArticleMarkdown(t *testing.T) {
result := NewInlineQueryResultArticleMarkdown("id", "title", "*message*") result := tgbotapi.NewInlineQueryResultArticleMarkdown("id", "title", "*message*")
if result.Type != "article" || if result.Type != "article" ||
result.ID != "id" || result.ID != "id" ||
result.Title != "title" || result.Title != "title" ||
result.InputMessageContent.(InputTextMessageContent).Text != "*message*" || result.InputMessageContent.(tgbotapi.InputTextMessageContent).Text != "*message*" ||
result.InputMessageContent.(InputTextMessageContent).ParseMode != "Markdown" { result.InputMessageContent.(tgbotapi.InputTextMessageContent).ParseMode != "Markdown" {
t.Fail() t.Fail()
} }
} }
func TestNewInlineQueryResultArticleHTML(t *testing.T) { func TestNewInlineQueryResultArticleHTML(t *testing.T) {
result := NewInlineQueryResultArticleHTML("id", "title", "<b>message</b>") result := tgbotapi.NewInlineQueryResultArticleHTML("id", "title", "<b>message</b>")
if result.Type != "article" || if result.Type != "article" ||
result.ID != "id" || result.ID != "id" ||
result.Title != "title" || result.Title != "title" ||
result.InputMessageContent.(InputTextMessageContent).Text != "<b>message</b>" || result.InputMessageContent.(tgbotapi.InputTextMessageContent).Text != "<b>message</b>" ||
result.InputMessageContent.(InputTextMessageContent).ParseMode != "HTML" { result.InputMessageContent.(tgbotapi.InputTextMessageContent).ParseMode != "HTML" {
t.Fail() t.Fail()
} }
} }
func TestNewInlineQueryResultGIF(t *testing.T) { func TestNewInlineQueryResultGIF(t *testing.T) {
result := NewInlineQueryResultGIF("id", "google.com") result := tgbotapi.NewInlineQueryResultGIF("id", "google.com")
if result.Type != "gif" || if result.Type != "gif" ||
result.ID != "id" || result.ID != "id" ||
@ -50,7 +52,7 @@ func TestNewInlineQueryResultGIF(t *testing.T) {
} }
func TestNewInlineQueryResultMPEG4GIF(t *testing.T) { func TestNewInlineQueryResultMPEG4GIF(t *testing.T) {
result := NewInlineQueryResultMPEG4GIF("id", "google.com") result := tgbotapi.NewInlineQueryResultMPEG4GIF("id", "google.com")
if result.Type != "mpeg4_gif" || if result.Type != "mpeg4_gif" ||
result.ID != "id" || result.ID != "id" ||
@ -60,7 +62,7 @@ func TestNewInlineQueryResultMPEG4GIF(t *testing.T) {
} }
func TestNewInlineQueryResultPhoto(t *testing.T) { func TestNewInlineQueryResultPhoto(t *testing.T) {
result := NewInlineQueryResultPhoto("id", "google.com") result := tgbotapi.NewInlineQueryResultPhoto("id", "google.com")
if result.Type != "photo" || if result.Type != "photo" ||
result.ID != "id" || result.ID != "id" ||
@ -70,7 +72,7 @@ func TestNewInlineQueryResultPhoto(t *testing.T) {
} }
func TestNewInlineQueryResultPhotoWithThumb(t *testing.T) { func TestNewInlineQueryResultPhotoWithThumb(t *testing.T) {
result := NewInlineQueryResultPhotoWithThumb("id", "google.com", "thumb.com") result := tgbotapi.NewInlineQueryResultPhotoWithThumb("id", "google.com", "thumb.com")
if result.Type != "photo" || if result.Type != "photo" ||
result.ID != "id" || result.ID != "id" ||
@ -81,7 +83,7 @@ func TestNewInlineQueryResultPhotoWithThumb(t *testing.T) {
} }
func TestNewInlineQueryResultVideo(t *testing.T) { func TestNewInlineQueryResultVideo(t *testing.T) {
result := NewInlineQueryResultVideo("id", "google.com") result := tgbotapi.NewInlineQueryResultVideo("id", "google.com")
if result.Type != "video" || if result.Type != "video" ||
result.ID != "id" || result.ID != "id" ||
@ -91,7 +93,7 @@ func TestNewInlineQueryResultVideo(t *testing.T) {
} }
func TestNewInlineQueryResultAudio(t *testing.T) { func TestNewInlineQueryResultAudio(t *testing.T) {
result := NewInlineQueryResultAudio("id", "google.com", "title") result := tgbotapi.NewInlineQueryResultAudio("id", "google.com", "title")
if result.Type != "audio" || if result.Type != "audio" ||
result.ID != "id" || result.ID != "id" ||
@ -102,7 +104,7 @@ func TestNewInlineQueryResultAudio(t *testing.T) {
} }
func TestNewInlineQueryResultVoice(t *testing.T) { func TestNewInlineQueryResultVoice(t *testing.T) {
result := NewInlineQueryResultVoice("id", "google.com", "title") result := tgbotapi.NewInlineQueryResultVoice("id", "google.com", "title")
if result.Type != "voice" || if result.Type != "voice" ||
result.ID != "id" || result.ID != "id" ||
@ -113,7 +115,7 @@ func TestNewInlineQueryResultVoice(t *testing.T) {
} }
func TestNewInlineQueryResultDocument(t *testing.T) { func TestNewInlineQueryResultDocument(t *testing.T) {
result := NewInlineQueryResultDocument("id", "google.com", "title", "mime/type") result := tgbotapi.NewInlineQueryResultDocument("id", "google.com", "title", "mime/type")
if result.Type != "document" || if result.Type != "document" ||
result.ID != "id" || result.ID != "id" ||
@ -125,7 +127,7 @@ func TestNewInlineQueryResultDocument(t *testing.T) {
} }
func TestNewInlineQueryResultLocation(t *testing.T) { func TestNewInlineQueryResultLocation(t *testing.T) {
result := NewInlineQueryResultLocation("id", "name", 40, 50) result := tgbotapi.NewInlineQueryResultLocation("id", "name", 40, 50)
if result.Type != "location" || if result.Type != "location" ||
result.ID != "id" || result.ID != "id" ||
@ -137,7 +139,7 @@ func TestNewInlineQueryResultLocation(t *testing.T) {
} }
func TestNewEditMessageText(t *testing.T) { func TestNewEditMessageText(t *testing.T) {
edit := NewEditMessageText(ChatID, ReplyToMessageID, "new text") edit := tgbotapi.NewEditMessageText(ChatID, ReplyToMessageID, "new text")
if edit.Text != "new text" || if edit.Text != "new text" ||
edit.BaseEdit.ChatID != ChatID || edit.BaseEdit.ChatID != ChatID ||
@ -147,7 +149,7 @@ func TestNewEditMessageText(t *testing.T) {
} }
func TestNewEditMessageCaption(t *testing.T) { func TestNewEditMessageCaption(t *testing.T) {
edit := NewEditMessageCaption(ChatID, ReplyToMessageID, "new caption") edit := tgbotapi.NewEditMessageCaption(ChatID, ReplyToMessageID, "new caption")
if edit.Caption != "new caption" || if edit.Caption != "new caption" ||
edit.BaseEdit.ChatID != ChatID || edit.BaseEdit.ChatID != ChatID ||
@ -157,15 +159,15 @@ func TestNewEditMessageCaption(t *testing.T) {
} }
func TestNewEditMessageReplyMarkup(t *testing.T) { func TestNewEditMessageReplyMarkup(t *testing.T) {
markup := InlineKeyboardMarkup{ markup := tgbotapi.InlineKeyboardMarkup{
InlineKeyboard: [][]InlineKeyboardButton{ InlineKeyboard: [][]tgbotapi.InlineKeyboardButton{
[]InlineKeyboardButton{ []tgbotapi.InlineKeyboardButton{
InlineKeyboardButton{Text: "test"}, tgbotapi.InlineKeyboardButton{Text: "test"},
}, },
}, },
} }
edit := NewEditMessageReplyMarkup(ChatID, ReplyToMessageID, markup) edit := tgbotapi.NewEditMessageReplyMarkup(ChatID, ReplyToMessageID, markup)
if edit.ReplyMarkup.InlineKeyboard[0][0].Text != "test" || if edit.ReplyMarkup.InlineKeyboard[0][0].Text != "test" ||
edit.BaseEdit.ChatID != ChatID || edit.BaseEdit.ChatID != ChatID ||

@ -1,100 +0,0 @@
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
}

@ -37,7 +37,6 @@ type Update struct {
CallbackQuery *CallbackQuery `json:"callback_query"` CallbackQuery *CallbackQuery `json:"callback_query"`
ShippingQuery *ShippingQuery `json:"shipping_query"` ShippingQuery *ShippingQuery `json:"shipping_query"`
PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query"` PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query"`
Poll *Poll `json:"poll"`
} }
// UpdatesChannel is the channel for getting updates. // UpdatesChannel is the channel for getting updates.
@ -98,12 +97,9 @@ type Chat struct {
FirstName string `json:"first_name"` // optional FirstName string `json:"first_name"` // optional
LastName string `json:"last_name"` // optional LastName string `json:"last_name"` // optional
AllMembersAreAdmins bool `json:"all_members_are_administrators"` // optional AllMembersAreAdmins bool `json:"all_members_are_administrators"` // optional
Photo *ChatPhoto `json:"photo"` // optional Photo *ChatPhoto `json:"photo"`
Description string `json:"description,omitempty"` // optional Description string `json:"description,omitempty"` // optional
InviteLink string `json:"invite_link,omitempty"` // optional InviteLink string `json:"invite_link,omitempty"` // optional
PinnedMessage *Message `json:"pinned_message"` // optional
StickerSetName string `json:"sticker_set_name"` // optional
CanSetStickerSet bool `json:"can_set_sticker_set"` // optional
} }
// IsPrivate returns if the Chat is a private conversation. // IsPrivate returns if the Chat is a private conversation.
@ -134,53 +130,45 @@ func (c Chat) ChatConfig() ChatConfig {
// Message is returned by almost every request, and contains data about // Message is returned by almost every request, and contains data about
// almost anything. // almost anything.
type Message struct { type Message struct {
MessageID int `json:"message_id"` MessageID int `json:"message_id"`
From *User `json:"from"` // optional From *User `json:"from"` // optional
Date int `json:"date"` Date int `json:"date"`
Chat *Chat `json:"chat"` Chat *Chat `json:"chat"`
ForwardFrom *User `json:"forward_from"` // optional ForwardFrom *User `json:"forward_from"` // optional
ForwardFromChat *Chat `json:"forward_from_chat"` // optional ForwardFromChat *Chat `json:"forward_from_chat"` // optional
ForwardFromMessageID int `json:"forward_from_message_id"` // optional ForwardFromMessageID int `json:"forward_from_message_id"` // optional
ForwardSignature string `json:"forward_signature"` // optional ForwardDate int `json:"forward_date"` // optional
ForwardSenderName string `json:"forward_sender_name"` // optional ReplyToMessage *Message `json:"reply_to_message"` // optional
ForwardDate int `json:"forward_date"` // optional EditDate int `json:"edit_date"` // optional
ReplyToMessage *Message `json:"reply_to_message"` // optional Text string `json:"text"` // optional
EditDate int `json:"edit_date"` // optional Entities *[]MessageEntity `json:"entities"` // optional
MediaGroupID string `json:"media_group_id"` // optional Audio *Audio `json:"audio"` // optional
AuthorSignature string `json:"author_signature"` // optional Document *Document `json:"document"` // optional
Text string `json:"text"` // optional Animation *ChatAnimation `json:"animation"` // optional
Entities []MessageEntity `json:"entities"` // optional Game *Game `json:"game"` // optional
CaptionEntities []MessageEntity `json:"caption_entities"` // optional Photo *[]PhotoSize `json:"photo"` // optional
Audio *Audio `json:"audio"` // optional Sticker *Sticker `json:"sticker"` // optional
Document *Document `json:"document"` // optional Video *Video `json:"video"` // optional
Animation *ChatAnimation `json:"animation"` // optional VideoNote *VideoNote `json:"video_note"` // optional
Game *Game `json:"game"` // optional Voice *Voice `json:"voice"` // optional
Photo []PhotoSize `json:"photo"` // optional Caption string `json:"caption"` // optional
Sticker *Sticker `json:"sticker"` // optional Contact *Contact `json:"contact"` // optional
Video *Video `json:"video"` // optional Location *Location `json:"location"` // optional
VideoNote *VideoNote `json:"video_note"` // optional Venue *Venue `json:"venue"` // optional
Voice *Voice `json:"voice"` // optional NewChatMembers *[]User `json:"new_chat_members"` // optional
Caption string `json:"caption"` // optional LeftChatMember *User `json:"left_chat_member"` // optional
Contact *Contact `json:"contact"` // optional NewChatTitle string `json:"new_chat_title"` // optional
Location *Location `json:"location"` // optional NewChatPhoto *[]PhotoSize `json:"new_chat_photo"` // optional
Venue *Venue `json:"venue"` // optional DeleteChatPhoto bool `json:"delete_chat_photo"` // optional
Poll *Poll `json:"poll"` // optional GroupChatCreated bool `json:"group_chat_created"` // optional
NewChatMembers []User `json:"new_chat_members"` // optional SuperGroupChatCreated bool `json:"supergroup_chat_created"` // optional
LeftChatMember *User `json:"left_chat_member"` // optional ChannelChatCreated bool `json:"channel_chat_created"` // optional
NewChatTitle string `json:"new_chat_title"` // optional MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional
NewChatPhoto []PhotoSize `json:"new_chat_photo"` // optional MigrateFromChatID int64 `json:"migrate_from_chat_id"` // optional
DeleteChatPhoto bool `json:"delete_chat_photo"` // optional PinnedMessage *Message `json:"pinned_message"` // optional
GroupChatCreated bool `json:"group_chat_created"` // optional Invoice *Invoice `json:"invoice"` // optional
SuperGroupChatCreated bool `json:"supergroup_chat_created"` // optional SuccessfulPayment *SuccessfulPayment `json:"successful_payment"` // optional
ChannelChatCreated bool `json:"channel_chat_created"` // optional PassportData *PassportData `json:"passport_data,omitempty"` // optional
MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional
MigrateFromChatID int64 `json:"migrate_from_chat_id"` // optional
PinnedMessage *Message `json:"pinned_message"` // optional
Invoice *Invoice `json:"invoice"` // optional
SuccessfulPayment *SuccessfulPayment `json:"successful_payment"` // optional
ConnectedWebsite string `json:"connected_website"` // optional
PassportData *PassportData `json:"passport_data,omitempty"` // optional
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup"` // optional
} }
// Time converts the message timestamp into a Time. // Time converts the message timestamp into a Time.
@ -190,11 +178,11 @@ func (m *Message) Time() time.Time {
// IsCommand returns true if message starts with a "bot_command" entity. // IsCommand returns true if message starts with a "bot_command" entity.
func (m *Message) IsCommand() bool { func (m *Message) IsCommand() bool {
if m.Entities == nil || len(m.Entities) == 0 { if m.Entities == nil || len(*m.Entities) == 0 {
return false return false
} }
entity := m.Entities[0] entity := (*m.Entities)[0]
return entity.Offset == 0 && entity.Type == "bot_command" return entity.Offset == 0 && entity.Type == "bot_command"
} }
@ -224,7 +212,7 @@ func (m *Message) CommandWithAt() string {
} }
// IsCommand() checks that the message begins with a bot_command entity // IsCommand() checks that the message begins with a bot_command entity
entity := m.Entities[0] entity := (*m.Entities)[0]
return m.Text[1:entity.Length] return m.Text[1:entity.Length]
} }
@ -243,8 +231,7 @@ func (m *Message) CommandArguments() string {
} }
// IsCommand() checks that the message begins with a bot_command entity // IsCommand() checks that the message begins with a bot_command entity
entity := m.Entities[0] entity := (*m.Entities)[0]
if len(m.Text) == entity.Length { if len(m.Text) == entity.Length {
return "" // The command makes up the whole message return "" // The command makes up the whole message
} }
@ -299,22 +286,6 @@ type Document struct {
// Sticker contains information about a sticker. // Sticker contains information about a sticker.
type Sticker struct { type Sticker struct {
FileID string `json:"file_id"`
Width int `json:"width"`
Height int `json:"height"`
Thumbnail *PhotoSize `json:"thumb"` // optional
Emoji string `json:"emoji"` // optional
SetName string `json:"set_name"` // optional
MaskPosition MaskPosition `json:"mask_position"` //optional
FileSize int `json:"file_size"` // optional
}
// MaskPosition is the position of a mask.
type MaskPosition struct {
Point string `json:"point"`
XShift float32 `json:"x_shift"`
YShift float32 `json:"y_shift"`
Scale float32 `json:"scale"`
FileID string `json:"file_id"` FileID string `json:"file_id"`
Width int `json:"width"` Width int `json:"width"`
Height int `json:"height"` Height int `json:"height"`
@ -372,7 +343,6 @@ type Contact struct {
FirstName string `json:"first_name"` FirstName string `json:"first_name"`
LastName string `json:"last_name"` // optional LastName string `json:"last_name"` // optional
UserID int `json:"user_id"` // optional UserID int `json:"user_id"` // optional
VCard string `json:"vcard"` // optional
} }
// Location contains information about a place. // Location contains information about a place.
@ -389,20 +359,6 @@ type Venue struct {
FoursquareID string `json:"foursquare_id"` // optional FoursquareID string `json:"foursquare_id"` // optional
} }
// PollOption contains information about one answer option in a poll.
type PollOption struct {
Text string `json:"text"`
VoterCount int `json:"voter_count"`
}
// Poll contains information about a poll.
type Poll struct {
ID string `json:"id"`
Question string `json:"question"`
Options []PollOption `json:"options"`
IsClosed bool `json:"is_closed"`
}
// UserProfilePhotos contains a set of user profile photos. // UserProfilePhotos contains a set of user profile photos.
type UserProfilePhotos struct { type UserProfilePhotos struct {
TotalCount int `json:"total_count"` TotalCount int `json:"total_count"`
@ -465,7 +421,6 @@ type InlineKeyboardMarkup struct {
type InlineKeyboardButton struct { type InlineKeyboardButton struct {
Text string `json:"text"` Text string `json:"text"`
URL *string `json:"url,omitempty"` // optional URL *string `json:"url,omitempty"` // optional
LoginURL *LoginURL `json:"login_url,omitempty"` // optional
CallbackData *string `json:"callback_data,omitempty"` // optional CallbackData *string `json:"callback_data,omitempty"` // optional
SwitchInlineQuery *string `json:"switch_inline_query,omitempty"` // optional SwitchInlineQuery *string `json:"switch_inline_query,omitempty"` // optional
SwitchInlineQueryCurrentChat *string `json:"switch_inline_query_current_chat,omitempty"` // optional SwitchInlineQueryCurrentChat *string `json:"switch_inline_query_current_chat,omitempty"` // optional
@ -473,14 +428,6 @@ type InlineKeyboardButton struct {
Pay bool `json:"pay,omitempty"` // optional Pay bool `json:"pay,omitempty"` // optional
} }
// LoginURL is the parameters for the login inline keyboard button type.
type LoginURL struct {
URL string `json:"url"`
ForwardText string `json:"forward_text"`
BotUsername string `json:"bot_username"`
RequestWriteAccess bool `json:"request_write_access"`
}
// CallbackQuery is data sent when a keyboard button with callback data // CallbackQuery is data sent when a keyboard button with callback data
// is clicked. // is clicked.
type CallbackQuery struct { type CallbackQuery struct {
@ -514,7 +461,6 @@ type ChatMember struct {
CanRestrictMembers bool `json:"can_restrict_members,omitempty"` // optional CanRestrictMembers bool `json:"can_restrict_members,omitempty"` // optional
CanPinMessages bool `json:"can_pin_messages,omitempty"` // optional CanPinMessages bool `json:"can_pin_messages,omitempty"` // optional
CanPromoteMembers bool `json:"can_promote_members,omitempty"` // optional CanPromoteMembers bool `json:"can_promote_members,omitempty"` // optional
IsChatMember bool `json:"is_member"` // optional
CanSendMessages bool `json:"can_send_messages,omitempty"` // optional CanSendMessages bool `json:"can_send_messages,omitempty"` // optional
CanSendMediaMessages bool `json:"can_send_media_messages,omitempty"` // optional CanSendMediaMessages bool `json:"can_send_media_messages,omitempty"` // optional
CanSendOtherMessages bool `json:"can_send_other_messages,omitempty"` // optional CanSendOtherMessages bool `json:"can_send_other_messages,omitempty"` // optional
@ -579,6 +525,27 @@ func (info WebhookInfo) IsSet() bool {
return info.URL != "" return info.URL != ""
} }
// InputMediaPhoto contains a photo for displaying as part of a media group.
type InputMediaPhoto struct {
Type string `json:"type"`
Media string `json:"media"`
Caption string `json:"caption"`
ParseMode string `json:"parse_mode"`
}
// InputMediaVideo contains a video for displaying as part of a media group.
type InputMediaVideo struct {
Type string `json:"type"`
Media string `json:"media"`
// thumb intentionally missing as it is not currently compatible
Caption string `json:"caption"`
ParseMode string `json:"parse_mode"`
Width int `json:"width"`
Height int `json:"height"`
Duration int `json:"duration"`
SupportsStreaming bool `json:"supports_streaming"`
}
// InlineQuery is a Query from Telegram for an inline request. // InlineQuery is a Query from Telegram for an inline request.
type InlineQuery struct { type InlineQuery struct {
ID string `json:"id"` ID string `json:"id"`
@ -709,27 +676,11 @@ type InlineQueryResultDocument struct {
// InlineQueryResultLocation is an inline query response location. // InlineQueryResultLocation is an inline query response location.
type InlineQueryResultLocation struct { type InlineQueryResultLocation struct {
Type string `json:"type"` // required Type string `json:"type"` // required
ID string `json:"id"` // required ID string `json:"id"` // required
Latitude float64 `json:"latitude"` // required Latitude float64 `json:"latitude"` // required
Longitude float64 `json:"longitude"` // required Longitude float64 `json:"longitude"` // required
LivePeriod int `json:"live_period"` // optional Title string `json:"title"` // required
Title string `json:"title"` // required
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
InputMessageContent interface{} `json:"input_message_content,omitempty"`
ThumbURL string `json:"thumb_url"`
ThumbWidth int `json:"thumb_width"`
ThumbHeight int `json:"thumb_height"`
}
// InlineQueryResultContact is an inline query response contact.
type InlineQueryResultContact struct {
Type string `json:"type"` // required
ID string `json:"id"` // required
PhoneNumber string `json:"phone_number"` // required
FirstName string `json:"first_name"` // required
LastName string `json:"last_name"`
VCard string `json:"vcard"`
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
InputMessageContent interface{} `json:"input_message_content,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"`
ThumbURL string `json:"thumb_url"` ThumbURL string `json:"thumb_url"`
@ -785,7 +736,6 @@ type InputContactMessageContent struct {
PhoneNumber string `json:"phone_number"` PhoneNumber string `json:"phone_number"`
FirstName string `json:"first_name"` FirstName string `json:"first_name"`
LastName string `json:"last_name"` LastName string `json:"last_name"`
VCard string `json:"vcard"`
} }
// Invoice contains basic information about an invoice. // Invoice contains basic information about an invoice.
@ -823,9 +773,9 @@ type OrderInfo struct {
// ShippingOption represents one shipping option. // ShippingOption represents one shipping option.
type ShippingOption struct { type ShippingOption struct {
ID string `json:"id"` ID string `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Prices []LabeledPrice `json:"prices"` Prices *[]LabeledPrice `json:"prices"`
} }
// SuccessfulPayment contains basic information about a successful payment. // SuccessfulPayment contains basic information about a successful payment.
@ -858,64 +808,12 @@ type PreCheckoutQuery struct {
OrderInfo *OrderInfo `json:"order_info,omitempty"` OrderInfo *OrderInfo `json:"order_info,omitempty"`
} }
// StickerSet is a collection of stickers.
type StickerSet struct {
Name string `json:"name"`
Title string `json:"title"`
ContainsMasks bool `json:"contains_masks"`
Stickers []Sticker `json:"stickers"`
}
// BaseInputMedia is a base type for the InputMedia types.
type BaseInputMedia struct {
Type string `json:"type"`
Media string `json:"media"`
Caption string `json:"caption"`
ParseMode string `json:"parse_mode"`
}
// InputMediaPhoto is a photo to send as part of a media group.
type InputMediaPhoto struct {
BaseInputMedia
}
// InputMediaVideo is a video to send as part of a media group.
type InputMediaVideo struct {
BaseInputMedia
Width int `json:"width"`
Height int `json:"height"`
Duration int `json:"duration"`
SupportsStreaming bool `json:"supports_streaming"`
}
// InputMediaAnimation is an animation to send as part of a media group.
type InputMediaAnimation struct {
BaseInputMedia
Width int `json:"width"`
Height int `json:"height"`
Duration int `json:"duration"`
}
// InputMediaAudio is a audio to send as part of a media group.
type InputMediaAudio struct {
BaseInputMedia
Duration int `json:"duration"`
Performer string `json:"performer"`
Title string `json:"title"`
}
// InputMediaDocument is a audio to send as part of a media group.
type InputMediaDocument struct {
BaseInputMedia
}
// Error is an error containing extra information returned by the Telegram API. // Error is an error containing extra information returned by the Telegram API.
type Error struct { type Error struct {
Message string Message string
ResponseParameters ResponseParameters
} }
// Error message string.
func (e Error) Error() string { func (e Error) Error() string {
return e.Message return e.Message
} }

@ -1,12 +1,14 @@
package tgbotapi package tgbotapi_test
import ( import (
"testing" "testing"
"time" "time"
"git.tomans.ru/Tomansru/telegram-bot-api"
) )
func TestUserStringWith(t *testing.T) { func TestUserStringWith(t *testing.T) {
user := User{ user := tgbotapi.User{
ID: 0, ID: 0,
FirstName: "Test", FirstName: "Test",
LastName: "Test", LastName: "Test",
@ -21,7 +23,7 @@ func TestUserStringWith(t *testing.T) {
} }
func TestUserStringWithUserName(t *testing.T) { func TestUserStringWithUserName(t *testing.T) {
user := User{ user := tgbotapi.User{
ID: 0, ID: 0,
FirstName: "Test", FirstName: "Test",
LastName: "Test", LastName: "Test",
@ -35,7 +37,7 @@ func TestUserStringWithUserName(t *testing.T) {
} }
func TestMessageTime(t *testing.T) { func TestMessageTime(t *testing.T) {
message := Message{Date: 0} message := tgbotapi.Message{Date: 0}
date := time.Unix(0, 0) date := time.Unix(0, 0)
if message.Time() != date { if message.Time() != date {
@ -44,33 +46,33 @@ func TestMessageTime(t *testing.T) {
} }
func TestMessageIsCommandWithCommand(t *testing.T) { func TestMessageIsCommandWithCommand(t *testing.T) {
message := Message{Text: "/command"} message := tgbotapi.Message{Text: "/command"}
message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
if !message.IsCommand() { if message.IsCommand() != true {
t.Fail() t.Fail()
} }
} }
func TestIsCommandWithText(t *testing.T) { func TestIsCommandWithText(t *testing.T) {
message := Message{Text: "some text"} message := tgbotapi.Message{Text: "some text"}
if message.IsCommand() { if message.IsCommand() != false {
t.Fail() t.Fail()
} }
} }
func TestIsCommandWithEmptyText(t *testing.T) { func TestIsCommandWithEmptyText(t *testing.T) {
message := Message{Text: ""} message := tgbotapi.Message{Text: ""}
if message.IsCommand() { if message.IsCommand() != false {
t.Fail() t.Fail()
} }
} }
func TestCommandWithCommand(t *testing.T) { func TestCommandWithCommand(t *testing.T) {
message := Message{Text: "/command"} message := tgbotapi.Message{Text: "/command"}
message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
if message.Command() != "command" { if message.Command() != "command" {
t.Fail() t.Fail()
@ -78,7 +80,7 @@ func TestCommandWithCommand(t *testing.T) {
} }
func TestCommandWithEmptyText(t *testing.T) { func TestCommandWithEmptyText(t *testing.T) {
message := Message{Text: ""} message := tgbotapi.Message{Text: ""}
if message.Command() != "" { if message.Command() != "" {
t.Fail() t.Fail()
@ -86,7 +88,7 @@ func TestCommandWithEmptyText(t *testing.T) {
} }
func TestCommandWithNonCommand(t *testing.T) { func TestCommandWithNonCommand(t *testing.T) {
message := Message{Text: "test text"} message := tgbotapi.Message{Text: "test text"}
if message.Command() != "" { if message.Command() != "" {
t.Fail() t.Fail()
@ -94,8 +96,8 @@ func TestCommandWithNonCommand(t *testing.T) {
} }
func TestCommandWithBotName(t *testing.T) { func TestCommandWithBotName(t *testing.T) {
message := Message{Text: "/command@testbot"} message := tgbotapi.Message{Text: "/command@testbot"}
message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}}
if message.Command() != "command" { if message.Command() != "command" {
t.Fail() t.Fail()
@ -103,8 +105,8 @@ func TestCommandWithBotName(t *testing.T) {
} }
func TestCommandWithAtWithBotName(t *testing.T) { func TestCommandWithAtWithBotName(t *testing.T) {
message := Message{Text: "/command@testbot"} message := tgbotapi.Message{Text: "/command@testbot"}
message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}}
if message.CommandWithAt() != "command@testbot" { if message.CommandWithAt() != "command@testbot" {
t.Fail() t.Fail()
@ -112,37 +114,37 @@ func TestCommandWithAtWithBotName(t *testing.T) {
} }
func TestMessageCommandArgumentsWithArguments(t *testing.T) { func TestMessageCommandArgumentsWithArguments(t *testing.T) {
message := Message{Text: "/command with arguments"} message := tgbotapi.Message{Text: "/command with arguments"}
message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
if message.CommandArguments() != "with arguments" { if message.CommandArguments() != "with arguments" {
t.Fail() t.Fail()
} }
} }
func TestMessageCommandArgumentsWithMalformedArguments(t *testing.T) { func TestMessageCommandArgumentsWithMalformedArguments(t *testing.T) {
message := Message{Text: "/command-without argument space"} message := tgbotapi.Message{Text: "/command-without argument space"}
message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}}
if message.CommandArguments() != "without argument space" { if message.CommandArguments() != "without argument space" {
t.Fail() t.Fail()
} }
} }
func TestMessageCommandArgumentsWithoutArguments(t *testing.T) { func TestMessageCommandArgumentsWithoutArguments(t *testing.T) {
message := Message{Text: "/command"} message := tgbotapi.Message{Text: "/command"}
if message.CommandArguments() != "" { if message.CommandArguments() != "" {
t.Fail() t.Fail()
} }
} }
func TestMessageCommandArgumentsForNonCommand(t *testing.T) { func TestMessageCommandArgumentsForNonCommand(t *testing.T) {
message := Message{Text: "test text"} message := tgbotapi.Message{Text: "test text"}
if message.CommandArguments() != "" { if message.CommandArguments() != "" {
t.Fail() t.Fail()
} }
} }
func TestMessageEntityParseURLGood(t *testing.T) { func TestMessageEntityParseURLGood(t *testing.T) {
entity := MessageEntity{URL: "https://www.google.com"} entity := tgbotapi.MessageEntity{URL: "https://www.google.com"}
if _, err := entity.ParseURL(); err != nil { if _, err := entity.ParseURL(); err != nil {
t.Fail() t.Fail()
@ -150,7 +152,7 @@ func TestMessageEntityParseURLGood(t *testing.T) {
} }
func TestMessageEntityParseURLBad(t *testing.T) { func TestMessageEntityParseURLBad(t *testing.T) {
entity := MessageEntity{URL: ""} entity := tgbotapi.MessageEntity{URL: ""}
if _, err := entity.ParseURL(); err == nil { if _, err := entity.ParseURL(); err == nil {
t.Fail() t.Fail()
@ -158,31 +160,31 @@ func TestMessageEntityParseURLBad(t *testing.T) {
} }
func TestChatIsPrivate(t *testing.T) { func TestChatIsPrivate(t *testing.T) {
chat := Chat{ID: 10, Type: "private"} chat := tgbotapi.Chat{ID: 10, Type: "private"}
if !chat.IsPrivate() { if chat.IsPrivate() != true {
t.Fail() t.Fail()
} }
} }
func TestChatIsGroup(t *testing.T) { func TestChatIsGroup(t *testing.T) {
chat := Chat{ID: 10, Type: "group"} chat := tgbotapi.Chat{ID: 10, Type: "group"}
if !chat.IsGroup() { if chat.IsGroup() != true {
t.Fail() t.Fail()
} }
} }
func TestChatIsChannel(t *testing.T) { func TestChatIsChannel(t *testing.T) {
chat := Chat{ID: 10, Type: "channel"} chat := tgbotapi.Chat{ID: 10, Type: "channel"}
if !chat.IsChannel() { if chat.IsChannel() != true {
t.Fail() t.Fail()
} }
} }
func TestChatIsSuperGroup(t *testing.T) { func TestChatIsSuperGroup(t *testing.T) {
chat := Chat{ID: 10, Type: "supergroup"} chat := tgbotapi.Chat{ID: 10, Type: "supergroup"}
if !chat.IsSuperGroup() { if !chat.IsSuperGroup() {
t.Fail() t.Fail()
@ -190,50 +192,9 @@ func TestChatIsSuperGroup(t *testing.T) {
} }
func TestFileLink(t *testing.T) { func TestFileLink(t *testing.T) {
file := File{FilePath: "test/test.txt"} file := tgbotapi.File{FilePath: "test/test.txt"}
if file.Link("token") != "https://api.telegram.org/file/bottoken/test/test.txt" { if file.Link("token") != "https://api.telegram.org/file/bottoken/test/test.txt" {
t.Fail() t.Fail()
} }
} }
// Ensure all configs are sendable
var (
_ Chattable = AnimationConfig{}
_ Chattable = AudioConfig{}
_ Chattable = CallbackConfig{}
_ Chattable = ChatActionConfig{}
_ Chattable = ContactConfig{}
_ Chattable = DeleteChatPhotoConfig{}
_ Chattable = DeleteChatStickerSetConfig{}
_ Chattable = DeleteMessageConfig{}
_ Chattable = DocumentConfig{}
_ Chattable = EditMessageCaptionConfig{}
_ Chattable = EditMessageLiveLocationConfig{}
_ Chattable = EditMessageReplyMarkupConfig{}
_ Chattable = EditMessageTextConfig{}
_ Chattable = ForwardConfig{}
_ Chattable = GameConfig{}
_ Chattable = GetGameHighScoresConfig{}
_ Chattable = InlineConfig{}
_ Chattable = InvoiceConfig{}
_ Chattable = KickChatMemberConfig{}
_ Chattable = LocationConfig{}
_ Chattable = MediaGroupConfig{}
_ Chattable = MessageConfig{}
_ Chattable = PhotoConfig{}
_ Chattable = PinChatMessageConfig{}
_ Chattable = SetChatDescriptionConfig{}
_ Chattable = SetChatPhotoConfig{}
_ Chattable = SetChatTitleConfig{}
_ Chattable = SetGameScoreConfig{}
_ Chattable = StickerConfig{}
_ Chattable = UnpinChatMessageConfig{}
_ Chattable = UpdateConfig{}
_ Chattable = UserProfilePhotosConfig{}
_ Chattable = VenueConfig{}
_ Chattable = VideoConfig{}
_ Chattable = VideoNoteConfig{}
_ Chattable = VoiceConfig{}
_ Chattable = WebhookConfig{}
)