summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWim <wim@42.be>2016-11-15 23:15:57 +0100
committerWim <wim@42.be>2016-11-15 23:15:57 +0100
commitcd18d89894903a92ac12e1a2e78582b543a4ab6e (patch)
treede70d2f4abb65992170b2d07e187ef4cb58e3c8c
parent449ed31e25f2c21ac9db258999a5a893889983eb (diff)
downloadmatterbridge-msglm-cd18d89894903a92ac12e1a2e78582b543a4ab6e.tar.gz
matterbridge-msglm-cd18d89894903a92ac12e1a2e78582b543a4ab6e.tar.bz2
matterbridge-msglm-cd18d89894903a92ac12e1a2e78582b543a4ab6e.zip
Add initial telegram support
-rw-r--r--bridge/bridge.go4
-rw-r--r--bridge/config/config.go1
-rw-r--r--bridge/telegram/telegram.go74
-rw-r--r--vendor/github.com/go-telegram-bot-api/telegram-bot-api/LICENSE.txt21
-rw-r--r--vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go650
-rw-r--r--vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go694
-rw-r--r--vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go598
-rw-r--r--vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go550
-rw-r--r--vendor/manifest16
9 files changed, 2608 insertions, 0 deletions
diff --git a/bridge/bridge.go b/bridge/bridge.go
index 4a422c75..8c715369 100644
--- a/bridge/bridge.go
+++ b/bridge/bridge.go
@@ -7,6 +7,7 @@ import (
"github.com/42wim/matterbridge/bridge/irc"
"github.com/42wim/matterbridge/bridge/mattermost"
"github.com/42wim/matterbridge/bridge/slack"
+ "github.com/42wim/matterbridge/bridge/telegram"
"github.com/42wim/matterbridge/bridge/xmpp"
"strings"
)
@@ -55,6 +56,9 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Brid
case "discord":
b.Config = cfg.Discord[name]
b.Bridger = bdiscord.New(cfg.Discord[name], bridge.Account, c)
+ case "telegram":
+ b.Config = cfg.Telegram[name]
+ b.Bridger = btelegram.New(cfg.Telegram[name], bridge.Account, c)
}
return b
}
diff --git a/bridge/config/config.go b/bridge/config/config.go
index d3d88115..5fed2f0a 100644
--- a/bridge/config/config.go
+++ b/bridge/config/config.go
@@ -78,6 +78,7 @@ type Config struct {
Gitter map[string]Protocol
Xmpp map[string]Protocol
Discord map[string]Protocol
+ Telegram map[string]Protocol
Gateway []Gateway
SameChannelGateway []SameChannelGateway
}
diff --git a/bridge/telegram/telegram.go b/bridge/telegram/telegram.go
new file mode 100644
index 00000000..aaffe839
--- /dev/null
+++ b/bridge/telegram/telegram.go
@@ -0,0 +1,74 @@
+package btelegram
+
+import (
+ "github.com/42wim/matterbridge/bridge/config"
+ log "github.com/Sirupsen/logrus"
+ "github.com/go-telegram-bot-api/telegram-bot-api"
+ "strconv"
+)
+
+type Btelegram struct {
+ c *tgbotapi.BotAPI
+ Config *config.Protocol
+ Remote chan config.Message
+ Account string
+}
+
+var flog *log.Entry
+var protocol = "telegram"
+
+func init() {
+ flog = log.WithFields(log.Fields{"module": protocol})
+}
+
+func New(cfg config.Protocol, account string, c chan config.Message) *Btelegram {
+ b := &Btelegram{}
+ b.Config = &cfg
+ b.Remote = c
+ b.Account = account
+ return b
+}
+
+func (b *Btelegram) Connect() error {
+ var err error
+ flog.Info("Connecting")
+ b.c, err = tgbotapi.NewBotAPI(b.Config.Token)
+ if err != nil {
+ flog.Debugf("%#v", err)
+ return err
+ }
+ updates, err := b.c.GetUpdatesChan(tgbotapi.NewUpdate(0))
+ if err != nil {
+ flog.Debugf("%#v", err)
+ return err
+ }
+ flog.Info("Connection succeeded")
+ go b.handleRecv(updates)
+ return nil
+}
+
+func (b *Btelegram) JoinChannel(channel string) error {
+ return nil
+}
+
+func (b *Btelegram) Send(msg config.Message) error {
+ flog.Debugf("Receiving %#v", msg)
+ chatid, err := strconv.ParseInt(msg.Channel, 10, 64)
+ if err != nil {
+ return err
+ }
+ m := tgbotapi.NewMessage(chatid, msg.Text)
+ _, err = b.c.Send(m)
+ return err
+}
+
+func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
+ for update := range updates {
+ if update.Message == nil {
+ continue
+ }
+ flog.Debugf("Sending message from %s on %s to gateway", update.Message.From.UserName, b.Account)
+ b.Remote <- config.Message{Username: update.Message.From.UserName, Text: update.Message.Text, Channel: strconv.FormatInt(update.Message.Chat.ID, 10), Account: b.Account}
+
+ }
+}
diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/LICENSE.txt b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/LICENSE.txt
new file mode 100644
index 00000000..b1fef93b
--- /dev/null
+++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Syfaro
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
new file mode 100644
index 00000000..c7e473fb
--- /dev/null
+++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
@@ -0,0 +1,650 @@
+// Package tgbotapi has functions and types used for interacting with
+// the Telegram Bot API.
+package tgbotapi
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/technoweenie/multipartstreamer"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "net/url"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// BotAPI allows you to interact with the Telegram Bot API.
+type BotAPI struct {
+ Token string `json:"token"`
+ Debug bool `json:"debug"`
+ Self User `json:"-"`
+ Client *http.Client `json:"-"`
+}
+
+// NewBotAPI creates a new BotAPI instance.
+//
+// It requires a token, provided by @BotFather on Telegram.
+func NewBotAPI(token string) (*BotAPI, error) {
+ return NewBotAPIWithClient(token, &http.Client{})
+}
+
+// NewBotAPIWithClient creates a new BotAPI instance
+// and allows you to pass a http.Client.
+//
+// It requires a token, provided by @BotFather on Telegram.
+func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
+ bot := &BotAPI{
+ Token: token,
+ Client: client,
+ }
+
+ self, err := bot.GetMe()
+ if err != nil {
+ return &BotAPI{}, err
+ }
+
+ bot.Self = self
+
+ return bot, nil
+}
+
+// MakeRequest makes a request to a specific endpoint with our token.
+func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
+ method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
+
+ resp, err := bot.Client.PostForm(method, params)
+ if err != nil {
+ return APIResponse{}, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode == http.StatusForbidden {
+ return APIResponse{}, errors.New(ErrAPIForbidden)
+ }
+
+ bytes, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return APIResponse{}, err
+ }
+
+ if bot.Debug {
+ log.Println(endpoint, string(bytes))
+ }
+
+ var apiResp APIResponse
+ json.Unmarshal(bytes, &apiResp)
+
+ if !apiResp.Ok {
+ return APIResponse{}, errors.New(apiResp.Description)
+ }
+
+ return apiResp, 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.
+//
+// Requires the parameter to hold the file not be in the params.
+// File should be a string to a file path, a FileBytes struct,
+// or a FileReader struct.
+//
+// Note that if your FileReader has a size set to -1, it will read
+// the file into memory to calculate a size.
+func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
+ ms := multipartstreamer.New()
+ ms.WriteFields(params)
+
+ switch f := file.(type) {
+ case string:
+ fileHandle, err := os.Open(f)
+ if err != nil {
+ return APIResponse{}, err
+ }
+ defer fileHandle.Close()
+
+ fi, err := os.Stat(f)
+ if err != nil {
+ return APIResponse{}, err
+ }
+
+ ms.WriteReader(fieldname, fileHandle.Name(), fi.Size(), fileHandle)
+ case FileBytes:
+ buf := bytes.NewBuffer(f.Bytes)
+ ms.WriteReader(fieldname, f.Name, int64(len(f.Bytes)), buf)
+ case FileReader:
+ if f.Size != -1 {
+ ms.WriteReader(fieldname, f.Name, f.Size, f.Reader)
+
+ break
+ }
+
+ data, err := ioutil.ReadAll(f.Reader)
+ if err != nil {
+ return APIResponse{}, err
+ }
+
+ buf := bytes.NewBuffer(data)
+
+ ms.WriteReader(fieldname, f.Name, int64(len(data)), buf)
+ default:
+ return APIResponse{}, errors.New(ErrBadFileType)
+ }
+
+ method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
+
+ req, err := http.NewRequest("POST", method, nil)
+ if err != nil {
+ return APIResponse{}, err
+ }
+
+ ms.SetupRequest(req)
+
+ res, err := bot.Client.Do(req)
+ if err != nil {
+ return APIResponse{}, err
+ }
+ defer res.Body.Close()
+
+ bytes, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return APIResponse{}, err
+ }
+
+ if bot.Debug {
+ log.Println(string(bytes))
+ }
+
+ var apiResp APIResponse
+ json.Unmarshal(bytes, &apiResp)
+
+ if !apiResp.Ok {
+ return APIResponse{}, errors.New(apiResp.Description)
+ }
+
+ return apiResp, nil
+}
+
+// GetFileDirectURL returns direct URL to file
+//
+// It requires the FileID.
+func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) {
+ file, err := bot.GetFile(FileConfig{fileID})
+
+ if err != nil {
+ return "", err
+ }
+
+ return file.Link(bot.Token), nil
+}
+
+// GetMe fetches the currently authenticated bot.
+//
+// This method is called upon creation to validate the token,
+// and so you may get this data from BotAPI.Self without the need for
+// another request.
+func (bot *BotAPI) GetMe() (User, error) {
+ resp, err := bot.MakeRequest("getMe", nil)
+ if err != nil {
+ return User{}, err
+ }
+
+ var user User
+ json.Unmarshal(resp.Result, &user)
+
+ bot.debugLog("getMe", nil, user)
+
+ return user, nil
+}
+
+// IsMessageToMe returns true if message directed to this bot.
+//
+// It requires the Message.
+func (bot *BotAPI) IsMessageToMe(message Message) bool {
+ return strings.Contains(message.Text, "@"+bot.Self.UserName)
+}
+
+// Send will send a Chattable item to Telegram.
+//
+// It requires the Chattable to send.
+func (bot *BotAPI) Send(c Chattable) (Message, error) {
+ 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)
+ }
+}
+
+// sendExisting will send a Message with an existing file to Telegram.
+func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) {
+ v, err := config.values()
+
+ if err != nil {
+ return Message{}, err
+ }
+
+ message, err := bot.makeMessageRequest(method, v)
+ if err != nil {
+ return Message{}, err
+ }
+
+ return message, nil
+}
+
+// uploadAndSend will send a Message with a new file to Telegram.
+func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) {
+ params, err := config.params()
+ if err != nil {
+ return Message{}, err
+ }
+
+ file := config.getFile()
+
+ resp, err := bot.UploadFile(method, params, config.name(), file)
+ if err != nil {
+ return Message{}, err
+ }
+
+ var message Message
+ json.Unmarshal(resp.Result, &message)
+
+ bot.debugLog(method, nil, message)
+
+ return message, nil
+}
+
+// sendFile determines if the file is using an existing file or uploading
+// a new file, then sends it as needed.
+func (bot *BotAPI) sendFile(config Fileable) (Message, error) {
+ if config.useExistingFile() {
+ return bot.sendExisting(config.method(), config)
+ }
+
+ 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 {
+ return Message{}, err
+ }
+
+ message, err := bot.makeMessageRequest(config.method(), v)
+
+ if err != nil {
+ return Message{}, err
+ }
+
+ return message, nil
+}
+
+// GetUserProfilePhotos gets a user's profile photos.
+//
+// It requires UserID.
+// Offset and Limit are optional.
+func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
+ 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("getUserProfilePhotos", v)
+ if err != nil {
+ return UserProfilePhotos{}, err
+ }
+
+ var profilePhotos UserProfilePhotos
+ json.Unmarshal(resp.Result, &profilePhotos)
+
+ bot.debugLog("GetUserProfilePhoto", v, profilePhotos)
+
+ return profilePhotos, nil
+}
+
+// GetFile returns a File which can download a file from Telegram.
+//
+// Requires FileID.
+func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
+ v := url.Values{}
+ v.Add("file_id", config.FileID)
+
+ resp, err := bot.MakeRequest("getFile", v)
+ if err != nil {
+ return File{}, err
+ }
+
+ var file File
+ json.Unmarshal(resp.Result, &file)
+
+ bot.debugLog("GetFile", v, file)
+
+ return file, nil
+}
+
+// GetUpdates fetches updates.
+// If a WebHook is set, this will not return any data!
+//
+// Offset, Limit, and Timeout are optional.
+// To avoid stale items, set Offset to one higher than the previous item.
+// Set Timeout to a large number to reduce requests so you can get updates
+// instantly instead of having to wait between requests.
+func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
+ 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("getUpdates", v)
+ if err != nil {
+ return []Update{}, err
+ }
+
+ var updates []Update
+ json.Unmarshal(resp.Result, &updates)
+
+ bot.debugLog("getUpdates", v, updates)
+
+ return updates, nil
+}
+
+// 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())
+
+ return bot.MakeRequest("setWebhook", v)
+ }
+
+ params := make(map[string]string)
+ params["url"] = config.URL.String()
+
+ resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate)
+ if err != nil {
+ return APIResponse{}, err
+ }
+
+ var apiResp APIResponse
+ json.Unmarshal(resp.Result, &apiResp)
+
+ if bot.Debug {
+ log.Printf("setWebhook resp: %+v\n", apiResp)
+ }
+
+ return apiResp, nil
+}
+
+// GetUpdatesChan starts and returns a channel for getting updates.
+func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (<-chan Update, error) {
+ updatesChan := make(chan Update, 100)
+
+ go func() {
+ for {
+ updates, err := bot.GetUpdates(config)
+ if err != nil {
+ log.Println(err)
+ log.Println("Failed to get updates, retrying in 3 seconds...")
+ time.Sleep(time.Second * 3)
+
+ continue
+ }
+
+ for _, update := range updates {
+ if update.UpdateID >= config.Offset {
+ config.Offset = update.UpdateID + 1
+ updatesChan <- update
+ }
+ }
+ }
+ }()
+
+ return updatesChan, nil
+}
+
+// ListenForWebhook registers a http handler for a webhook.
+func (bot *BotAPI) ListenForWebhook(pattern string) <-chan Update {
+ updatesChan := make(chan Update, 100)
+
+ http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
+ bytes, _ := ioutil.ReadAll(r.Body)
+
+ var update Update
+ json.Unmarshal(bytes, &update)
+
+ updatesChan <- update
+ })
+
+ return updatesChan
+}
+
+// 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)
+ v.Add("text", config.Text)
+ v.Add("show_alert", strconv.FormatBool(config.ShowAlert))
+
+ 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 ChatMemberConfig) (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))
+
+ 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.
+func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) {
+ 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("getChat", v)
+ if err != nil {
+ return Chat{}, err
+ }
+
+ var chat Chat
+ err = json.Unmarshal(resp.Result, &chat)
+
+ bot.debugLog("getChat", v, chat)
+
+ return chat, err
+}
+
+// GetChatAdministrators gets a list of administrators in the chat.
+//
+// If none have been appointed, only the creator will be returned.
+// Bots are not shown, even if they are an administrator.
+func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error) {
+ 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("getChatAdministrators", v)
+ if err != nil {
+ return []ChatMember{}, err
+ }
+
+ var members []ChatMember
+ err = json.Unmarshal(resp.Result, &members)
+
+ bot.debugLog("getChatAdministrators", v, members)
+
+ return members, err
+}
+
+// GetChatMembersCount gets the number of users in a chat.
+func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) {
+ 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("getChatMembersCount", v)
+ if err != nil {
+ return -1, err
+ }
+
+ var count int
+ err = json.Unmarshal(resp.Result, &count)
+
+ bot.debugLog("getChatMembersCount", v, count)
+
+ return count, err
+}
+
+// GetChatMember gets a specific chat member.
+func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, 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))
+
+ resp, err := bot.MakeRequest("getChatMember", v)
+ if err != nil {
+ return ChatMember{}, err
+ }
+
+ var member ChatMember
+ err = json.Unmarshal(resp.Result, &member)
+
+ bot.debugLog("getChatMember", v, member)
+
+ return member, err
+}
+
+// UnbanChatMember unbans a user from a chat. Note that this only will work
+// in supergroups, 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", strconv.FormatInt(config.ChatID, 10))
+ } else {
+ v.Add("chat_id", config.SuperGroupUsername)
+ }
+ v.Add("user_id", strconv.Itoa(config.UserID))
+
+ bot.debugLog("unbanChatMember", v, nil)
+
+ return bot.MakeRequest("unbanChatMember", v)
+}
diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
new file mode 100644
index 00000000..b91d0704
--- /dev/null
+++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
@@ -0,0 +1,694 @@
+package tgbotapi
+
+import (
+ "encoding/json"
+ "io"
+ "net/url"
+ "strconv"
+)
+
+// Telegram constants
+const (
+ // APIEndpoint is the endpoint for all API methods,
+ // with formatting for Sprintf.
+ APIEndpoint = "https://api.telegram.org/bot%s/%s"
+ // FileEndpoint is the endpoint for downloading a file from Telegram.
+ FileEndpoint = "https://api.telegram.org/file/bot%s/%s"
+)
+
+// Constant values for ChatActions
+const (
+ ChatTyping = "typing"
+ ChatUploadPhoto = "upload_photo"
+ ChatRecordVideo = "record_video"
+ ChatUploadVideo = "upload_video"
+ ChatRecordAudio = "record_audio"
+ ChatUploadAudio = "upload_audio"
+ ChatUploadDocument = "upload_document"
+ ChatFindLocation = "find_location"
+)
+
+// API errors
+const (
+ // ErrAPIForbidden happens when a token is bad
+ ErrAPIForbidden = "forbidden"
+)
+
+// Constant values for ParseMode in MessageConfig
+const (
+ ModeMarkdown = "Markdown"
+ ModeHTML = "HTML"
+)
+
+// Library errors
+const (
+ // ErrBadFileType happens when you pass an unknown type
+ ErrBadFileType = "bad file type"
+ ErrBadURL = "bad or empty url"
+)
+
+// Chattable is any config type that can be sent.
+type Chattable interface {
+ values() (url.Values, error)
+ method() string
+}
+
+// Fileable is any config type that can be sent that includes a file.
+type Fileable interface {
+ Chattable
+ params() (map[string]string, error)
+ name() string
+ getFile() interface{}
+ useExistingFile() bool
+}
+
+// BaseChat is base type for all chat config types.
+type BaseChat struct {
+ ChatID int64 // required
+ ChannelUsername string
+ ReplyToMessageID int
+ ReplyMarkup interface{}
+ DisableNotification bool
+}
+
+// values returns url.Values representation of BaseChat
+func (chat *BaseChat) values() (url.Values, error) {
+ v := url.Values{}
+ if chat.ChannelUsername != "" {
+ v.Add("chat_id", chat.ChannelUsername)
+ } else {
+ v.Add("chat_id", strconv.FormatInt(chat.ChatID, 10))
+ }
+
+ if chat.ReplyToMessageID != 0 {
+ v.Add("reply_to_message_id", strconv.Itoa(chat.ReplyToMessageID))
+ }
+
+ if chat.ReplyMarkup != nil {
+ data, err := json.Marshal(chat.ReplyMarkup)
+ if err != nil {
+ return v, err
+ }
+
+ v.Add("reply_markup", string(data))
+ }
+
+ v.Add("disable_notification", strconv.FormatBool(chat.DisableNotification))
+
+ return v, nil
+}
+
+// BaseFile is a base type for all file config types.
+type BaseFile struct {
+ BaseChat
+ File interface{}
+ FileID string
+ UseExisting bool
+ MimeType string
+ FileSize int
+}
+
+// params returns a map[string]string representation of BaseFile.
+func (file BaseFile) params() (map[string]string, error) {
+ params := make(map[string]string)
+
+ if file.ChannelUsername != "" {
+ params["chat_id"] = file.ChannelUsername
+ } else {
+ params["chat_id"] = strconv.FormatInt(file.ChatID, 10)
+ }
+
+ if file.ReplyToMessageID != 0 {
+ params["reply_to_message_id"] = strconv.Itoa(file.ReplyToMessageID)
+ }
+
+ if file.ReplyMarkup != nil {
+ data, err := json.Marshal(file.ReplyMarkup)
+ if err != nil {
+ return params, err
+ }
+
+ params["reply_markup"] = string(data)
+ }
+
+ if file.MimeType != "" {
+ params["mime_type"] = file.MimeType
+ }
+
+ if file.FileSize > 0 {
+ params["file_size"] = strconv.Itoa(file.FileSize)
+ }
+
+ params["disable_notification"] = strconv.FormatBool(file.DisableNotification)
+
+ return params, nil
+}
+
+// getFile returns the file.
+func (file BaseFile) getFile() interface{} {
+ return file.File
+}
+
+// useExistingFile returns if the BaseFile has already been uploaded.
+func (file BaseFile) useExistingFile() bool {
+ return file.UseExisting
+}
+
+// BaseEdit is base type of all chat edits.
+type BaseEdit struct {
+ ChatID int64
+ ChannelUsername string
+ MessageID int
+ InlineMessageID string
+ ReplyMarkup *InlineKeyboardMarkup
+}
+
+func (edit BaseEdit) values() (url.Values, error) {
+ v := url.Values{}
+
+ if edit.InlineMessageID == "" {
+ if edit.ChannelUsername != "" {
+ v.Add("chat_id", edit.ChannelUsername)
+ } else {
+ v.Add("chat_id", strconv.FormatInt(edit.ChatID, 10))
+ }
+ v.Add("message_id", strconv.Itoa(edit.MessageID))
+ } else {
+ v.Add("inline_message_id", edit.InlineMessageID)
+ }
+
+ if edit.ReplyMarkup != nil {
+ data, err := json.Marshal(edit.ReplyMarkup)
+ if err != nil {
+ return v, err
+ }
+ v.Add("reply_markup", string(data))
+ }
+
+ return v, nil
+}
+
+// MessageConfig contains information about a SendMessage request.
+type MessageConfig struct {
+ BaseChat
+ Text string
+ ParseMode string
+ DisableWebPagePreview bool
+}
+
+// values returns a url.Values representation of MessageConfig.
+func (config MessageConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+ v.Add("text", config.Text)
+ v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview))
+ if config.ParseMode != "" {
+ v.Add("parse_mode", config.ParseMode)
+ }
+
+ return v, nil
+}
+
+// method returns Telegram API method name for sending Message.
+func (config MessageConfig) method() string {
+ return "sendMessage"
+}
+
+// ForwardConfig contains information about a ForwardMessage request.
+type ForwardConfig struct {
+ BaseChat
+ FromChatID int64 // required
+ FromChannelUsername string
+ MessageID int // required
+}
+
+// values returns a url.Values representation of ForwardConfig.
+func (config ForwardConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+ v.Add("from_chat_id", strconv.FormatInt(config.FromChatID, 10))
+ v.Add("message_id", strconv.Itoa(config.MessageID))
+ return v, nil
+}
+
+// method returns Telegram API method name for sending Forward.
+func (config ForwardConfig) method() string {
+ return "forwardMessage"
+}
+
+// PhotoConfig contains information about a SendPhoto request.
+type PhotoConfig struct {
+ BaseFile
+ Caption string
+}
+
+// Params returns a map[string]string representation of PhotoConfig.
+func (config PhotoConfig) params() (map[string]string, error) {
+ params, _ := config.BaseFile.params()
+
+ if config.Caption != "" {
+ params["caption"] = config.Caption
+ }
+
+ return params, nil
+}
+
+// Values returns a url.Values representation of PhotoConfig.
+func (config PhotoConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+
+ v.Add(config.name(), config.FileID)
+ if config.Caption != "" {
+ v.Add("caption", config.Caption)
+ }
+ return v, nil
+}
+
+// name returns the field name for the Photo.
+func (config PhotoConfig) name() string {
+ return "photo"
+}
+
+// method returns Telegram API method name for sending Photo.
+func (config PhotoConfig) method() string {
+ return "sendPhoto"
+}
+
+// AudioConfig contains information about a SendAudio request.
+type AudioConfig struct {
+ BaseFile
+ Duration int
+ Performer string
+ Title string
+}
+
+// values returns a url.Values representation of AudioConfig.
+func (config AudioConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+
+ v.Add(config.name(), config.FileID)
+ if config.Duration != 0 {
+ v.Add("duration", strconv.Itoa(config.Duration))
+ }
+
+ if config.Performer != "" {
+ v.Add("performer", config.Performer)
+ }
+ if config.Title != "" {
+ v.Add("title", config.Title)
+ }
+
+ return v, nil
+}
+
+// params returns a map[string]string representation of AudioConfig.
+func (config AudioConfig) params() (map[string]string, error) {
+ params, _ := config.BaseFile.params()
+
+ if config.Duration != 0 {
+ params["duration"] = strconv.Itoa(config.Duration)
+ }
+
+ if config.Performer != "" {
+ params["performer"] = config.Performer
+ }
+ if config.Title != "" {
+ params["title"] = config.Title
+ }
+
+ return params, nil
+}
+
+// name returns the field name for the Audio.
+func (config AudioConfig) name() string {
+ return "audio"
+}
+
+// method returns Telegram API method name for sending Audio.
+func (config AudioConfig) method() string {
+ return "sendAudio"
+}
+
+// DocumentConfig contains information about a SendDocument request.
+type DocumentConfig struct {
+ BaseFile
+}
+
+// values returns a url.Values representation of DocumentConfig.
+func (config DocumentConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+
+ v.Add(config.name(), config.FileID)
+
+ return v, nil
+}
+
+// params returns a map[string]string representation of DocumentConfig.
+func (config DocumentConfig) params() (map[string]string, error) {
+ params, _ := config.BaseFile.params()
+
+ return params, nil
+}
+
+// name returns the field name for the Document.
+func (config DocumentConfig) name() string {
+ return "document"
+}
+
+// method returns Telegram API method name for sending Document.
+func (config DocumentConfig) method() string {
+ return "sendDocument"
+}
+
+// StickerConfig contains information about a SendSticker request.
+type StickerConfig struct {
+ BaseFile
+}
+
+// values returns a url.Values representation of StickerConfig.
+func (config StickerConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+
+ v.Add(config.name(), config.FileID)
+
+ return v, nil
+}
+
+// params returns a map[string]string representation of StickerConfig.
+func (config StickerConfig) params() (map[string]string, error) {
+ params, _ := config.BaseFile.params()
+
+ return params, nil
+}
+
+// name returns the field name for the Sticker.
+func (config StickerConfig) name() string {
+ return "sticker"
+}
+
+// method returns Telegram API method name for sending Sticker.
+func (config StickerConfig) method() string {
+ return "sendSticker"
+}
+
+// VideoConfig contains information about a SendVideo request.
+type VideoConfig struct {
+ BaseFile
+ Duration int
+ Caption string
+}
+
+// values returns a url.Values representation of VideoConfig.
+func (config VideoConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+
+ v.Add(config.name(), config.FileID)
+ if config.Duration != 0 {
+ v.Add("duration", strconv.Itoa(config.Duration))
+ }
+ if config.Caption != "" {
+ v.Add("caption", config.Caption)
+ }
+
+ return v, nil
+}
+
+// params returns a map[string]string representation of VideoConfig.
+func (config VideoConfig) params() (map[string]string, error) {
+ params, _ := config.BaseFile.params()
+
+ return params, nil
+}
+
+// name returns the field name for the Video.
+func (config VideoConfig) name() string {
+ return "video"
+}
+
+// method returns Telegram API method name for sending Video.
+func (config VideoConfig) method() string {
+ return "sendVideo"
+}
+
+// VoiceConfig contains information about a SendVoice request.
+type VoiceConfig struct {
+ BaseFile
+ Duration int
+}
+
+// values returns a url.Values representation of VoiceConfig.
+func (config VoiceConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+
+ v.Add(config.name(), config.FileID)
+ if config.Duration != 0 {
+ v.Add("duration", strconv.Itoa(config.Duration))
+ }
+
+ return v, nil
+}
+
+// params returns a map[string]string representation of VoiceConfig.
+func (config VoiceConfig) params() (map[string]string, error) {
+ params, _ := config.BaseFile.params()
+
+ if config.Duration != 0 {
+ params["duration"] = strconv.Itoa(config.Duration)
+ }
+
+ return params, nil
+}
+
+// name returns the field name for the Voice.
+func (config VoiceConfig) name() string {
+ return "voice"
+}
+
+// method returns Telegram API method name for sending Voice.
+func (config VoiceConfig) method() string {
+ return "sendVoice"
+}
+
+// LocationConfig contains information about a SendLocation request.
+type LocationConfig struct {
+ BaseChat
+ Latitude float64 // required
+ Longitude float64 // required
+}
+
+// values returns a url.Values representation of LocationConfig.
+func (config LocationConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+
+ v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
+ v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64))
+
+ return v, nil
+}
+
+// method returns Telegram API method name for sending Location.
+func (config LocationConfig) method() string {
+ return "sendLocation"
+}
+
+// VenueConfig contains information about a SendVenue request.
+type VenueConfig struct {
+ BaseChat
+ Latitude float64 // required
+ Longitude float64 // required
+ Title string // required
+ Address string // required
+ FoursquareID string
+}
+
+func (config VenueConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+
+ v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
+ v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64))
+ v.Add("title", config.Title)
+ v.Add("address", config.Address)
+ if config.FoursquareID != "" {
+ v.Add("foursquare_id", config.FoursquareID)
+ }
+
+ return v, nil
+}
+
+func (config VenueConfig) method() string {
+ return "sendVenue"
+}
+
+// ContactConfig allows you to send a contact.
+type ContactConfig struct {
+ BaseChat
+ PhoneNumber string
+ FirstName string
+ LastName string
+}
+
+func (config ContactConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+
+ v.Add("phone_number", config.PhoneNumber)
+ v.Add("first_name", config.FirstName)
+ v.Add("last_name", config.LastName)
+
+ return v, nil
+}
+
+func (config ContactConfig) method() string {
+ return "sendContact"
+}
+
+// ChatActionConfig contains information about a SendChatAction request.
+type ChatActionConfig struct {
+ BaseChat
+ Action string // required
+}
+
+// values returns a url.Values representation of ChatActionConfig.
+func (config ChatActionConfig) values() (url.Values, error) {
+ v, _ := config.BaseChat.values()
+ v.Add("action", config.Action)
+ return v, nil
+}
+
+// method returns Telegram API method name for sending ChatAction.
+func (config ChatActionConfig) method() string {
+ return "sendChatAction"
+}
+
+// EditMessageTextConfig allows you to modify the text in a message.
+type EditMessageTextConfig struct {
+ BaseEdit
+ Text string
+ ParseMode string
+ DisableWebPagePreview bool
+}
+
+func (config EditMessageTextConfig) values() (url.Values, error) {
+ v, _ := config.BaseEdit.values()
+
+ v.Add("text", config.Text)
+ v.Add("parse_mode", config.ParseMode)
+ v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview))
+
+ return v, nil
+}
+
+func (config EditMessageTextConfig) method() string {
+ return "editMessageText"
+}
+
+// EditMessageCaptionConfig allows you to modify the caption of a message.
+type EditMessageCaptionConfig struct {
+ BaseEdit
+ Caption string
+}
+
+func (config EditMessageCaptionConfig) values() (url.Values, error) {
+ v, _ := config.BaseEdit.values()
+
+ v.Add("caption", config.Caption)
+
+ return v, nil
+}
+
+func (config EditMessageCaptionConfig) method() string {
+ return "editMessageCaption"
+}
+
+// EditMessageReplyMarkupConfig allows you to modify the reply markup
+// of a message.
+type EditMessageReplyMarkupConfig struct {
+ BaseEdit
+}
+
+func (config EditMessageReplyMarkupConfig) values() (url.Values, error) {
+ return config.BaseEdit.values()
+}
+
+func (config EditMessageReplyMarkupConfig) method() string {
+ return "editMessageReplyMarkup"
+}
+
+// UserProfilePhotosConfig contains information about a
+// GetUserProfilePhotos request.
+type UserProfilePhotosConfig struct {
+ UserID int
+ Offset int
+ Limit int
+}
+
+// FileConfig has information about a file hosted on Telegram.
+type FileConfig struct {
+ FileID string
+}
+
+// UpdateConfig contains information about a GetUpdates request.
+type UpdateConfig struct {
+ Offset int
+ Limit int
+ Timeout int
+}
+
+// WebhookConfig contains information about a SetWebhook request.
+type WebhookConfig struct {
+ URL *url.URL
+ Certificate interface{}
+}
+
+// FileBytes contains information about a set of bytes to upload
+// as a File.
+type FileBytes struct {
+ Name string
+ Bytes []byte
+}
+
+// FileReader contains information about a reader to upload as a File.
+// If Size is -1, it will read the entire Reader into memory to
+// calculate a Size.
+type FileReader struct {
+ Name string
+ Reader io.Reader
+ Size int64
+}
+
+// InlineConfig contains information on making an InlineQuery response.
+type InlineConfig struct {
+ InlineQueryID string `json:"inline_query_id"`
+ Results []interface{} `json:"results"`
+ CacheTime int `json:"cache_time"`
+ IsPersonal bool `json:"is_personal"`
+ NextOffset string `json:"next_offset"`
+ SwitchPMText string `json:"switch_pm_text"`
+ SwitchPMParameter string `json:"switch_pm_parameter"`
+}
+
+// CallbackConfig contains information on making a CallbackQuery response.
+type CallbackConfig struct {
+ CallbackQueryID string `json:"callback_query_id"`
+ Text string `json:"text"`
+ ShowAlert bool `json:"show_alert"`
+}
+
+// ChatMemberConfig contains information about a user in a chat for use
+// with administrative functions such as kicking or unbanning a user.
+type ChatMemberConfig struct {
+ ChatID int64
+ SuperGroupUsername string
+ UserID int
+}
+
+// ChatConfig contains information about getting information on a chat.
+type ChatConfig struct {
+ ChatID int64
+ SuperGroupUsername string
+}
+
+// ChatConfigWithUser contains information about getting information on
+// a specific user within a chat.
+type ChatConfigWithUser struct {
+ ChatID int64
+ SuperGroupUsername string
+ UserID int
+}
diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
new file mode 100644
index 00000000..53e50052
--- /dev/null
+++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
@@ -0,0 +1,598 @@
+package tgbotapi
+
+import (
+ "net/url"
+)
+
+// NewMessage creates a new Message.
+//
+// chatID is where to send it, text is the message text.
+func NewMessage(chatID int64, text string) MessageConfig {
+ return MessageConfig{
+ BaseChat: BaseChat{
+ ChatID: chatID,
+ ReplyToMessageID: 0,
+ },
+ Text: text,
+ DisableWebPagePreview: false,
+ }
+}
+
+// NewMessageToChannel creates a new Message that is sent to a channel
+// by username.
+// username is the username of the channel, text is the message text.
+func NewMessageToChannel(username string, text string) MessageConfig {
+ return MessageConfig{
+ BaseChat: BaseChat{
+ ChannelUsername: username,
+ },
+ Text: text,
+ }
+}
+
+// NewForward creates a new forward.
+//
+// chatID is where to send it, fromChatID is the source chat,
+// and messageID is the ID of the original message.
+func NewForward(chatID int64, fromChatID int64, messageID int) ForwardConfig {
+ return ForwardConfig{
+ BaseChat: BaseChat{ChatID: chatID},
+ FromChatID: fromChatID,
+ MessageID: messageID,
+ }
+}
+
+// NewPhotoUpload creates a new photo uploader.
+//
+// chatID is where to send it, file is a string path to the file,
+// FileReader, or FileBytes.
+//
+// Note that you must send animated GIFs as a document.
+func NewPhotoUpload(chatID int64, file interface{}) PhotoConfig {
+ return PhotoConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ File: file,
+ UseExisting: false,
+ },
+ }
+}
+
+// NewPhotoShare shares an existing photo.
+// You may use this to reshare an existing photo without reuploading it.
+//
+// chatID is where to send it, fileID is the ID of the file
+// already uploaded.
+func NewPhotoShare(chatID int64, fileID string) PhotoConfig {
+ return PhotoConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ FileID: fileID,
+ UseExisting: true,
+ },
+ }
+}
+
+// NewAudioUpload creates a new audio uploader.
+//
+// chatID is where to send it, file is a string path to the file,
+// FileReader, or FileBytes.
+func NewAudioUpload(chatID int64, file interface{}) AudioConfig {
+ return AudioConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ File: file,
+ UseExisting: false,
+ },
+ }
+}
+
+// NewAudioShare shares an existing audio file.
+// You may use this to reshare an existing audio file without
+// reuploading it.
+//
+// chatID is where to send it, fileID is the ID of the audio
+// already uploaded.
+func NewAudioShare(chatID int64, fileID string) AudioConfig {
+ return AudioConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ FileID: fileID,
+ UseExisting: true,
+ },
+ }
+}
+
+// NewDocumentUpload creates a new document uploader.
+//
+// chatID is where to send it, file is a string path to the file,
+// FileReader, or FileBytes.
+func NewDocumentUpload(chatID int64, file interface{}) DocumentConfig {
+ return DocumentConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ File: file,
+ UseExisting: false,
+ },
+ }
+}
+
+// NewDocumentShare shares an existing document.
+// You may use this to reshare an existing document without
+// reuploading it.
+//
+// chatID is where to send it, fileID is the ID of the document
+// already uploaded.
+func NewDocumentShare(chatID int64, fileID string) DocumentConfig {
+ return DocumentConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ FileID: fileID,
+ UseExisting: true,
+ },
+ }
+}
+
+// NewStickerUpload creates a new sticker uploader.
+//
+// chatID is where to send it, file is a string path to the file,
+// FileReader, or FileBytes.
+func NewStickerUpload(chatID int64, file interface{}) StickerConfig {
+ return StickerConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ File: file,
+ UseExisting: false,
+ },
+ }
+}
+
+// NewStickerShare shares an existing sticker.
+// You may use this to reshare an existing sticker without
+// reuploading it.
+//
+// chatID is where to send it, fileID is the ID of the sticker
+// already uploaded.
+func NewStickerShare(chatID int64, fileID string) StickerConfig {
+ return StickerConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ FileID: fileID,
+ UseExisting: true,
+ },
+ }
+}
+
+// NewVideoUpload creates a new video uploader.
+//
+// chatID is where to send it, file is a string path to the file,
+// FileReader, or FileBytes.
+func NewVideoUpload(chatID int64, file interface{}) VideoConfig {
+ return VideoConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ File: file,
+ UseExisting: false,
+ },
+ }
+}
+
+// NewVideoShare shares an existing video.
+// You may use this to reshare an existing video without reuploading it.
+//
+// chatID is where to send it, fileID is the ID of the video
+// already uploaded.
+func NewVideoShare(chatID int64, fileID string) VideoConfig {
+ return VideoConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ FileID: fileID,
+ UseExisting: true,
+ },
+ }
+}
+
+// NewVoiceUpload creates a new voice uploader.
+//
+// chatID is where to send it, file is a string path to the file,
+// FileReader, or FileBytes.
+func NewVoiceUpload(chatID int64, file interface{}) VoiceConfig {
+ return VoiceConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ File: file,
+ UseExisting: false,
+ },
+ }
+}
+
+// NewVoiceShare shares an existing voice.
+// You may use this to reshare an existing voice without reuploading it.
+//
+// chatID is where to send it, fileID is the ID of the video
+// already uploaded.
+func NewVoiceShare(chatID int64, fileID string) VoiceConfig {
+ return VoiceConfig{
+ BaseFile: BaseFile{
+ BaseChat: BaseChat{ChatID: chatID},
+ FileID: fileID,
+ UseExisting: true,
+ },
+ }
+}
+
+// NewContact allows you to send a shared contact.
+func NewContact(chatID int64, phoneNumber, firstName string) ContactConfig {
+ return ContactConfig{
+ BaseChat: BaseChat{
+ ChatID: chatID,
+ },
+ PhoneNumber: phoneNumber,
+ FirstName: firstName,
+ }
+}
+
+// NewLocation shares your location.
+//
+// chatID is where to send it, latitude and longitude are coordinates.
+func NewLocation(chatID int64, latitude float64, longitude float64) LocationConfig {
+ return LocationConfig{
+ BaseChat: BaseChat{
+ ChatID: chatID,
+ },
+ Latitude: latitude,
+ Longitude: longitude,
+ }
+}
+
+// NewVenue allows you to send a venue and its location.
+func NewVenue(chatID int64, title, address string, latitude, longitude float64) VenueConfig {
+ return VenueConfig{
+ BaseChat: BaseChat{
+ ChatID: chatID,
+ },
+ Title: title,
+ Address: address,
+ Latitude: latitude,
+ Longitude: longitude,
+ }
+}
+
+// NewChatAction sets a chat action.
+// Actions last for 5 seconds, or until your next action.
+//
+// chatID is where to send it, action should be set via Chat constants.
+func NewChatAction(chatID int64, action string) ChatActionConfig {
+ return ChatActionConfig{
+ BaseChat: BaseChat{ChatID: chatID},
+ Action: action,
+ }
+}
+
+// NewUserProfilePhotos gets user profile photos.
+//
+// userID is the ID of the user you wish to get profile photos from.
+func NewUserProfilePhotos(userID int) UserProfilePhotosConfig {
+ return UserProfilePhotosConfig{
+ UserID: userID,
+ Offset: 0,
+ Limit: 0,
+ }
+}
+
+// NewUpdate gets updates since the last Offset.
+//
+// offset is the last Update ID to include.
+// You likely want to set this to the last Update ID plus 1.
+func NewUpdate(offset int) UpdateConfig {
+ return UpdateConfig{
+ Offset: offset,
+ Limit: 0,
+ Timeout: 0,
+ }
+}
+
+// NewWebhook creates a new webhook.
+//
+// link is the url parsable link you wish to get the updates.
+func NewWebhook(link string) WebhookConfig {
+ u, _ := url.Parse(link)
+
+ return WebhookConfig{
+ URL: u,
+ }
+}
+
+// NewWebhookWithCert creates a new webhook with a certificate.
+//
+// link is the url you wish to get webhooks,
+// file contains a string to a file, FileReader, or FileBytes.
+func NewWebhookWithCert(link string, file interface{}) WebhookConfig {
+ u, _ := url.Parse(link)
+
+ return WebhookConfig{
+ URL: u,
+ Certificate: file,
+ }
+}
+
+// NewInlineQueryResultArticle creates a new inline query article.
+func NewInlineQueryResultArticle(id, title, messageText string) InlineQueryResultArticle {
+ return InlineQueryResultArticle{
+ Type: "article",
+ ID: id,
+ Title: title,
+ InputMessageContent: InputTextMessageContent{
+ Text: messageText,
+ },
+ }
+}
+
+// NewInlineQueryResultArticleMarkdown creates a new inline query article with Markdown parsing.
+func NewInlineQueryResultArticleMarkdown(id, title, messageText string) InlineQueryResultArticle {
+ return InlineQueryResultArticle{
+ Type: "article",
+ ID: id,
+ Title: title,
+ InputMessageContent: InputTextMessageContent{
+ Text: messageText,
+ ParseMode: "Markdown",
+ },
+ }
+}
+
+// NewInlineQueryResultArticleHTML creates a new inline query article with HTML parsing.
+func NewInlineQueryResultArticleHTML(id, title, messageText string) InlineQueryResultArticle {
+ return InlineQueryResultArticle{
+ Type: "article",
+ ID: id,
+ Title: title,
+ InputMessageContent: InputTextMessageContent{
+ Text: messageText,
+ ParseMode: "HTML",
+ },
+ }
+}
+
+// NewInlineQueryResultGIF creates a new inline query GIF.
+func NewInlineQueryResultGIF(id, url string) InlineQueryResultGIF {
+ return InlineQueryResultGIF{
+ Type: "gif",
+ ID: id,
+ URL: url,
+ }
+}
+
+// NewInlineQueryResultMPEG4GIF creates a new inline query MPEG4 GIF.
+func NewInlineQueryResultMPEG4GIF(id, url string) InlineQueryResultMPEG4GIF {
+ return InlineQueryResultMPEG4GIF{
+ Type: "mpeg4_gif",
+ ID: id,
+ URL: url,
+ }
+}
+
+// NewInlineQueryResultPhoto creates a new inline query photo.
+func NewInlineQueryResultPhoto(id, url string) InlineQueryResultPhoto {
+ return InlineQueryResultPhoto{
+ Type: "photo",
+ ID: id,
+ URL: url,
+ }
+}
+
+// NewInlineQueryResultPhotoWithThumb creates a new inline query photo.
+func NewInlineQueryResultPhotoWithThumb(id, url, thumb string) InlineQueryResultPhoto {
+ return InlineQueryResultPhoto{
+ Type: "photo",
+ ID: id,
+ URL: url,
+ ThumbURL: thumb,
+ }
+}
+
+// NewInlineQueryResultVideo creates a new inline query video.
+func NewInlineQueryResultVideo(id, url string) InlineQueryResultVideo {
+ return InlineQueryResultVideo{
+ Type: "video",
+ ID: id,
+ URL: url,
+ }
+}
+
+// NewInlineQueryResultAudio creates a new inline query audio.
+func NewInlineQueryResultAudio(id, url, title string) InlineQueryResultAudio {
+ return InlineQueryResultAudio{
+ Type: "audio",
+ ID: id,
+ URL: url,
+ Title: title,
+ }
+}
+
+// NewInlineQueryResultVoice creates a new inline query voice.
+func NewInlineQueryResultVoice(id, url, title string) InlineQueryResultVoice {
+ return InlineQueryResultVoice{
+ Type: "voice",
+ ID: id,
+ URL: url,
+ Title: title,
+ }
+}
+
+// NewInlineQueryResultDocument creates a new inline query document.
+func NewInlineQueryResultDocument(id, url, title, mimeType string) InlineQueryResultDocument {
+ return InlineQueryResultDocument{
+ Type: "document",
+ ID: id,
+ URL: url,
+ Title: title,
+ MimeType: mimeType,
+ }
+}
+
+// NewInlineQueryResultLocation creates a new inline query location.
+func NewInlineQueryResultLocation(id, title string, latitude, longitude float64) InlineQueryResultLocation {
+ return InlineQueryResultLocation{
+ Type: "location",
+ ID: id,
+ Title: title,
+ Latitude: latitude,
+ Longitude: longitude,
+ }
+}
+
+// NewEditMessageText allows you to edit the text of a message.
+func NewEditMessageText(chatID int64, messageID int, text string) EditMessageTextConfig {
+ return EditMessageTextConfig{
+ BaseEdit: BaseEdit{
+ ChatID: chatID,
+ MessageID: messageID,
+ },
+ Text: text,
+ }
+}
+
+// NewEditMessageCaption allows you to edit the caption of a message.
+func NewEditMessageCaption(chatID int64, messageID int, caption string) EditMessageCaptionConfig {
+ return EditMessageCaptionConfig{
+ BaseEdit: BaseEdit{
+ ChatID: chatID,
+ MessageID: messageID,
+ },
+ Caption: caption,
+ }
+}
+
+// NewEditMessageReplyMarkup allows you to edit the inline
+// keyboard markup.
+func NewEditMessageReplyMarkup(chatID int64, messageID int, replyMarkup InlineKeyboardMarkup) EditMessageReplyMarkupConfig {
+ return EditMessageReplyMarkupConfig{
+ BaseEdit: BaseEdit{
+ ChatID: chatID,
+ MessageID: messageID,
+ ReplyMarkup: &replyMarkup,
+ },
+ }
+}
+
+// NewHideKeyboard hides the keyboard, with the option for being selective
+// or hiding for everyone.
+func NewHideKeyboard(selective bool) ReplyKeyboardHide {
+ return ReplyKeyboardHide{
+ HideKeyboard: true,
+ Selective: selective,
+ }
+}
+
+// NewKeyboardButton creates a regular keyboard button.
+func NewKeyboardButton(text string) KeyboardButton {
+ return KeyboardButton{
+ Text: text,
+ }
+}
+
+// NewKeyboardButtonContact creates a keyboard button that requests
+// user contact information upon click.
+func NewKeyboardButtonContact(text string) KeyboardButton {
+ return KeyboardButton{
+ Text: text,
+ RequestContact: true,
+ }
+}
+
+// NewKeyboardButtonLocation creates a keyboard button that requests
+// user location information upon click.
+func NewKeyboardButtonLocation(text string) KeyboardButton {
+ return KeyboardButton{
+ Text: text,
+ RequestLocation: true,
+ }
+}
+
+// NewKeyboardButtonRow creates a row of keyboard buttons.
+func NewKeyboardButtonRow(buttons ...KeyboardButton) []KeyboardButton {
+ var row []KeyboardButton
+
+ row = append(row, buttons...)
+
+ return row
+}
+
+// NewReplyKeyboard creates a new regular keyboard with sane defaults.
+func NewReplyKeyboard(rows ...[]KeyboardButton) ReplyKeyboardMarkup {
+ var keyboard [][]KeyboardButton
+
+ keyboard = append(keyboard, rows...)
+
+ return ReplyKeyboardMarkup{
+ ResizeKeyboard: true,
+ Keyboard: keyboard,
+ }
+}
+
+// NewInlineKeyboardButtonData creates an inline keyboard button with text
+// and data for a callback.
+func NewInlineKeyboardButtonData(text, data string) InlineKeyboardButton {
+ return InlineKeyboardButton{
+ Text: text,
+ CallbackData: &data,
+ }
+}
+
+// NewInlineKeyboardButtonURL creates an inline keyboard button with text
+// which goes to a URL.
+func NewInlineKeyboardButtonURL(text, url string) InlineKeyboardButton {
+ return InlineKeyboardButton{
+ Text: text,
+ URL: &url,
+ }
+}
+
+// NewInlineKeyboardButtonSwitch creates an inline keyboard button with
+// text which allows the user to switch to a chat or return to a chat.
+func NewInlineKeyboardButtonSwitch(text, sw string) InlineKeyboardButton {
+ return InlineKeyboardButton{
+ Text: text,
+ SwitchInlineQuery: &sw,
+ }
+}
+
+// NewInlineKeyboardRow creates an inline keyboard row with buttons.
+func NewInlineKeyboardRow(buttons ...InlineKeyboardButton) []InlineKeyboardButton {
+ var row []InlineKeyboardButton
+
+ row = append(row, buttons...)
+
+ return row
+}
+
+// NewInlineKeyboardMarkup creates a new inline keyboard.
+func NewInlineKeyboardMarkup(rows ...[]InlineKeyboardButton) InlineKeyboardMarkup {
+ var keyboard [][]InlineKeyboardButton
+
+ keyboard = append(keyboard, rows...)
+
+ return InlineKeyboardMarkup{
+ InlineKeyboard: keyboard,
+ }
+}
+
+// NewCallback creates a new callback message.
+func NewCallback(id, text string) CallbackConfig {
+ return CallbackConfig{
+ CallbackQueryID: id,
+ Text: text,
+ ShowAlert: false,
+ }
+}
+
+// NewCallbackWithAlert creates a new callback message that alerts
+// the user.
+func NewCallbackWithAlert(id, text string) CallbackConfig {
+ return CallbackConfig{
+ CallbackQueryID: id,
+ Text: text,
+ ShowAlert: true,
+ }
+}
diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
new file mode 100644
index 00000000..1c7b1b0f
--- /dev/null
+++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
@@ -0,0 +1,550 @@
+package tgbotapi
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/url"
+ "strings"
+ "time"
+)
+
+// APIResponse is a response from the Telegram API with the result
+// stored raw.
+type APIResponse struct {
+ Ok bool `json:"ok"`
+ Result json.RawMessage `json:"result"`
+ ErrorCode int `json:"error_code"`
+ Description string `json:"description"`
+}
+
+// Update is an update response, from GetUpdates.
+type Update struct {
+ UpdateID int `json:"update_id"`
+ Message *Message `json:"message"`
+ EditedMessage *Message `json:"edited_message"`
+ InlineQuery *InlineQuery `json:"inline_query"`
+ ChosenInlineResult *ChosenInlineResult `json:"chosen_inline_result"`
+ CallbackQuery *CallbackQuery `json:"callback_query"`
+}
+
+// User is a user on Telegram.
+type User struct {
+ ID int `json:"id"`
+ FirstName string `json:"first_name"`
+ LastName string `json:"last_name"` // optional
+ UserName string `json:"username"` // optional
+}
+
+// String displays a simple text version of a user.
+//
+// It is normally a user's username, but falls back to a first/last
+// name as available.
+func (u *User) String() string {
+ if u.UserName != "" {
+ return u.UserName
+ }
+
+ name := u.FirstName
+ if u.LastName != "" {
+ name += " " + u.LastName
+ }
+
+ return name
+}
+
+// GroupChat is a group chat.
+type GroupChat struct {
+ ID int `json:"id"`
+ Title string `json:"title"`
+}
+
+// Chat contains information about the place a message was sent.
+type Chat struct {
+ ID int64 `json:"id"`
+ Type string `json:"type"`
+ Title string `json:"title"` // optional
+ UserName string `json:"username"` // optional
+ FirstName string `json:"first_name"` // optional
+ LastName string `json:"last_name"` // optional
+}
+
+// IsPrivate returns if the Chat is a private conversation.
+func (c Chat) IsPrivate() bool {
+ return c.Type == "private"
+}
+
+// IsGroup returns if the Chat is a group.
+func (c Chat) IsGroup() bool {
+ return c.Type == "group"
+}
+
+// IsSuperGroup returns if the Chat is a supergroup.
+func (c Chat) IsSuperGroup() bool {
+ return c.Type == "supergroup"
+}
+
+// IsChannel returns if the Chat is a channel.
+func (c Chat) IsChannel() bool {
+ return c.Type == "channel"
+}
+
+// ChatConfig returns a ChatConfig struct for chat related methods.
+func (c Chat) ChatConfig() ChatConfig {
+ return ChatConfig{ChatID: c.ID}
+}
+
+// Message is returned by almost every request, and contains data about
+// almost anything.
+type Message struct {
+ MessageID int `json:"message_id"`
+ From *User `json:"from"` // optional
+ Date int `json:"date"`
+ Chat *Chat `json:"chat"`
+ ForwardFrom *User `json:"forward_from"` // optional
+ ForwardFromChat *Chat `json:"forward_from_chat"` // optional
+ ForwardDate int `json:"forward_date"` // optional
+ ReplyToMessage *Message `json:"reply_to_message"` // optional
+ EditDate int `json:"edit_date"` // optional
+ Text string `json:"text"` // optional
+ Entities *[]MessageEntity `json:"entities"` // optional
+ Audio *Audio `json:"audio"` // optional
+ Document *Document `json:"document"` // optional
+ Photo *[]PhotoSize `json:"photo"` // optional
+ Sticker *Sticker `json:"sticker"` // optional
+ Video *Video `json:"video"` // optional
+ Voice *Voice `json:"voice"` // optional
+ Caption string `json:"caption"` // optional
+ Contact *Contact `json:"contact"` // optional
+ Location *Location `json:"location"` // optional
+ Venue *Venue `json:"venue"` // optional
+ NewChatMember *User `json:"new_chat_member"` // optional
+ LeftChatMember *User `json:"left_chat_member"` // optional
+ NewChatTitle string `json:"new_chat_title"` // optional
+ NewChatPhoto *[]PhotoSize `json:"new_chat_photo"` // optional
+ DeleteChatPhoto bool `json:"delete_chat_photo"` // optional
+ GroupChatCreated bool `json:"group_chat_created"` // optional
+ SuperGroupChatCreated bool `json:"supergroup_chat_created"` // optional
+ ChannelChatCreated bool `json:"channel_chat_created"` // optional
+ MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional
+ MigrateFromChatID int64 `json:"migrate_from_chat_id"` // optional
+ PinnedMessage *Message `json:"pinned_message"` // optional
+}
+
+// Time converts the message timestamp into a Time.
+func (m *Message) Time() time.Time {
+ return time.Unix(int64(m.Date), 0)
+}
+
+// IsCommand returns true if message starts with '/'.
+func (m *Message) IsCommand() bool {
+ return m.Text != "" && m.Text[0] == '/'
+}
+
+// Command checks if the message was a command and if it was, returns the
+// command. If the Message was not a command, it returns an empty string.
+//
+// If the command contains the at bot syntax, it removes the bot name.
+func (m *Message) Command() string {
+ if !m.IsCommand() {
+ return ""
+ }
+
+ command := strings.SplitN(m.Text, " ", 2)[0][1:]
+
+ if i := strings.Index(command, "@"); i != -1 {
+ command = command[:i]
+ }
+
+ return command
+}
+
+// CommandArguments checks if the message was a command and if it was,
+// returns all text after the command name. If the Message was not a
+// command, it returns an empty string.
+func (m *Message) CommandArguments() string {
+ if !m.IsCommand() {
+ return ""
+ }
+
+ split := strings.SplitN(m.Text, " ", 2)
+ if len(split) != 2 {
+ return ""
+ }
+
+ return strings.SplitN(m.Text, " ", 2)[1]
+}
+
+// MessageEntity contains information about data in a Message.
+type MessageEntity struct {
+ Type string `json:"type"`
+ Offset int `json:"offset"`
+ Length int `json:"length"`
+ URL string `json:"url"` // optional
+ User *User `json:"user"` // optional
+}
+
+// ParseURL attempts to parse a URL contained within a MessageEntity.
+func (entity MessageEntity) ParseURL() (*url.URL, error) {
+ if entity.URL == "" {
+ return nil, errors.New(ErrBadURL)
+ }
+
+ return url.Parse(entity.URL)
+}
+
+// PhotoSize contains information about photos.
+type PhotoSize struct {
+ FileID string `json:"file_id"`
+ Width int `json:"width"`
+ Height int `json:"height"`
+ FileSize int `json:"file_size"` // optional
+}
+
+// Audio contains information about audio.
+type Audio struct {
+ FileID string `json:"file_id"`
+ Duration int `json:"duration"`
+ Performer string `json:"performer"` // optional
+ Title string `json:"title"` // optional
+ MimeType string `json:"mime_type"` // optional
+ FileSize int `json:"file_size"` // optional
+}
+
+// Document contains information about a document.
+type Document struct {
+ FileID string `json:"file_id"`
+ Thumbnail *PhotoSize `json:"thumb"` // optional
+ FileName string `json:"file_name"` // optional
+ MimeType string `json:"mime_type"` // optional
+ FileSize int `json:"file_size"` // optional
+}
+
+// Sticker contains information about a sticker.
+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
+ FileSize int `json:"file_size"` // optional
+}
+
+// Video contains information about a video.
+type Video struct {
+ FileID string `json:"file_id"`
+ Width int `json:"width"`
+ Height int `json:"height"`
+ Duration int `json:"duration"`
+ Thumbnail *PhotoSize `json:"thumb"` // optional
+ MimeType string `json:"mime_type"` // optional
+ FileSize int `json:"file_size"` // optional
+}
+
+// Voice contains information about a voice.
+type Voice struct {
+ FileID string `json:"file_id"`
+ Duration int `json:"duration"`
+ MimeType string `json:"mime_type"` // optional
+ FileSize int `json:"file_size"` // optional
+}
+
+// Contact contains information about a contact.
+//
+// Note that LastName and UserID may be empty.
+type Contact struct {
+ PhoneNumber string `json:"phone_number"`
+ FirstName string `json:"first_name"`
+ LastName string `json:"last_name"` // optional
+ UserID int `json:"user_id"` // optional
+}
+
+// Location contains information about a place.
+type Location struct {
+ Longitude float64 `json:"longitude"`
+ Latitude float64 `json:"latitude"`
+}
+
+// Venue contains information about a venue, including its Location.
+type Venue struct {
+ Location Location `json:"location"`
+ Title string `json:"title"`
+ Address string `json:"address"`
+ FoursquareID string `json:"foursquare_id"` // optional
+}
+
+// UserProfilePhotos contains a set of user profile photos.
+type UserProfilePhotos struct {
+ TotalCount int `json:"total_count"`
+ Photos [][]PhotoSize `json:"photos"`
+}
+
+// File contains information about a file to download from Telegram.
+type File struct {
+ FileID string `json:"file_id"`
+ FileSize int `json:"file_size"` // optional
+ FilePath string `json:"file_path"` // optional
+}
+
+// Link returns a full path to the download URL for a File.
+//
+// It requires the Bot Token to create the link.
+func (f *File) Link(token string) string {
+ return fmt.Sprintf(FileEndpoint, token, f.FilePath)
+}
+
+// ReplyKeyboardMarkup allows the Bot to set a custom keyboard.
+type ReplyKeyboardMarkup struct {
+ Keyboard [][]KeyboardButton `json:"keyboard"`
+ ResizeKeyboard bool `json:"resize_keyboard"` // optional
+ OneTimeKeyboard bool `json:"one_time_keyboard"` // optional
+ Selective bool `json:"selective"` // optional
+}
+
+// KeyboardButton is a button within a custom keyboard.
+type KeyboardButton struct {
+ Text string `json:"text"`
+ RequestContact bool `json:"request_contact"`
+ RequestLocation bool `json:"request_location"`
+}
+
+// ReplyKeyboardHide allows the Bot to hide a custom keyboard.
+type ReplyKeyboardHide struct {
+ HideKeyboard bool `json:"hide_keyboard"`
+ Selective bool `json:"selective"` // optional
+}
+
+// InlineKeyboardMarkup is a custom keyboard presented for an inline bot.
+type InlineKeyboardMarkup struct {
+ InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"`
+}
+
+// InlineKeyboardButton is a button within a custom keyboard for
+// inline query responses.
+//
+// Note that some values are references as even an empty string
+// will change behavior.
+type InlineKeyboardButton struct {
+ Text string `json:"text"`
+ URL *string `json:"url,omitempty"` // optional
+ CallbackData *string `json:"callback_data,omitempty"` // optional
+ SwitchInlineQuery *string `json:"switch_inline_query,omitempty"` // optional
+}
+
+// CallbackQuery is data sent when a keyboard button with callback data
+// is clicked.
+type CallbackQuery struct {
+ ID string `json:"id"`
+ From *User `json:"from"`
+ Message *Message `json:"message"` // optional
+ InlineMessageID string `json:"inline_message_id"` // optional
+ Data string `json:"data"` // optional
+}
+
+// ForceReply allows the Bot to have users directly reply to it without
+// additional interaction.
+type ForceReply struct {
+ ForceReply bool `json:"force_reply"`
+ Selective bool `json:"selective"` // optional
+}
+
+// ChatMember is information about a member in a chat.
+type ChatMember struct {
+ User *User `json:"user"`
+ Status string `json:"status"`
+}
+
+// IsCreator returns if the ChatMember was the creator of the chat.
+func (chat ChatMember) IsCreator() bool { return chat.Status == "creator" }
+
+// IsAdministrator returns if the ChatMember is a chat administrator.
+func (chat ChatMember) IsAdministrator() bool { return chat.Status == "administrator" }
+
+// IsMember returns if the ChatMember is a current member of the chat.
+func (chat ChatMember) IsMember() bool { return chat.Status == "member" }
+
+// HasLeft returns if the ChatMember left the chat.
+func (chat ChatMember) HasLeft() bool { return chat.Status == "left" }
+
+// WasKicked returns if the ChatMember was kicked from the chat.
+func (chat ChatMember) WasKicked() bool { return chat.Status == "kicked" }
+
+// InlineQuery is a Query from Telegram for an inline request.
+type InlineQuery struct {
+ ID string `json:"id"`
+ From *User `json:"from"`
+ Location *Location `json:"location"` // optional
+ Query string `json:"query"`
+ Offset string `json:"offset"`
+}
+
+// InlineQueryResultArticle is an inline query response article.
+type InlineQueryResultArticle struct {
+ Type string `json:"type"` // required
+ ID string `json:"id"` // required
+ Title string `json:"title"` // required
+ InputMessageContent interface{} `json:"input_message_content,omitempty"` // required
+ ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
+ URL string `json:"url"`
+ HideURL bool `json:"hide_url"`
+ Description string `json:"description"`
+ ThumbURL string `json:"thumb_url"`
+ ThumbWidth int `json:"thumb_width"`
+ ThumbHeight int `json:"thumb_height"`
+}
+
+// InlineQueryResultPhoto is an inline query response photo.
+type InlineQueryResultPhoto struct {
+ Type string `json:"type"` // required
+ ID string `json:"id"` // required
+ URL string `json:"photo_url"` // required
+ MimeType string `json:"mime_type"`
+ Width int `json:"photo_width"`
+ Height int `json:"photo_height"`
+ ThumbURL string `json:"thumb_url"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ Caption string `json:"caption"`
+ ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
+ InputMessageContent interface{} `json:"input_message_content,omitempty"`
+}
+
+// InlineQueryResultGIF is an inline query response GIF.
+type InlineQueryResultGIF struct {
+ Type string `json:"type"` // required
+ ID string `json:"id"` // required
+ URL string `json:"gif_url"` // required
+ Width int `json:"gif_width"`
+ Height int `json:"gif_height"`
+ ThumbURL string `json:"thumb_url"`
+ Title string `json:"title"`
+ Caption string `json:"caption"`
+ ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
+ InputMessageContent interface{} `json:"input_message_content,omitempty"`
+}
+
+// InlineQueryResultMPEG4GIF is an inline query response MPEG4 GIF.
+type InlineQueryResultMPEG4GIF struct {
+ Type string `json:"type"` // required
+ ID string `json:"id"` // required
+ URL string `json:"mpeg4_url"` // required
+ Width int `json:"mpeg4_width"`
+ Height int `json:"mpeg4_height"`
+ ThumbURL string `json:"thumb_url"`
+ Title string `json:"title"`
+ Caption string `json:"caption"`
+ ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
+ InputMessageContent interface{} `json:"input_message_content,omitempty"`
+}
+
+// InlineQueryResultVideo is an inline query response video.
+type InlineQueryResultVideo struct {
+ Type string `json:"type"` // required
+ ID string `json:"id"` // required
+ URL string `json:"video_url"` // required
+ MimeType string `json:"mime_type"` // required
+ ThumbURL string `json:"thumb_url"`
+ Title string `json:"title"`
+ Caption string `json:"caption"`
+ Width int `json:"video_width"`
+ Height int `json:"video_height"`
+ Duration int `json:"video_duration"`
+ Description string `json:"description"`
+ ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
+ InputMessageContent interface{} `json:"input_message_content,omitempty"`
+}
+
+// InlineQueryResultAudio is an inline query response audio.
+type InlineQueryResultAudio struct {
+ Type string `json:"type"` // required
+ ID string `json:"id"` // required
+ URL string `json:"audio_url"` // required
+ Title string `json:"title"` // required
+ Performer string `json:"performer"`
+ Duration int `json:"audio_duration"`
+ ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
+ InputMessageContent interface{} `json:"input_message_content,omitempty"`
+}
+
+// InlineQueryResultVoice is an inline query response voice.
+type InlineQueryResultVoice struct {
+ Type string `json:"type"` // required
+ ID string `json:"id"` // required
+ URL string `json:"voice_url"` // required
+ Title string `json:"title"` // required
+ Duration int `json:"voice_duration"`
+ ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
+ InputMessageContent interface{} `json:"input_message_content,omitempty"`
+}
+
+// InlineQueryResultDocument is an inline query response document.
+type InlineQueryResultDocument struct {
+ Type string `json:"type"` // required
+ ID string `json:"id"` // required
+ Title string `json:"title"` // required
+ Caption string `json:"caption"`
+ URL string `json:"document_url"` // required
+ MimeType string `json:"mime_type"` // required
+ Description string `json:"description"`
+ 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"`
+}
+
+// InlineQueryResultLocation is an inline query response location.
+type InlineQueryResultLocation struct {
+ Type string `json:"type"` // required
+ ID string `json:"id"` // required
+ Latitude float64 `json:"latitude"` // required
+ Longitude float64 `json:"longitude"` // 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"`
+}
+
+// ChosenInlineResult is an inline query result chosen by a User
+type ChosenInlineResult struct {
+ ResultID string `json:"result_id"`
+ From *User `json:"from"`
+ Location *Location `json:"location"`
+ InlineMessageID string `json:"inline_message_id"`
+ Query string `json:"query"`
+}
+
+// InputTextMessageContent contains text for displaying
+// as an inline query result.
+type InputTextMessageContent struct {
+ Text string `json:"message_text"`
+ ParseMode string `json:"parse_mode"`
+ DisableWebPagePreview bool `json:"disable_web_page_preview"`
+}
+
+// InputLocationMessageContent contains a location for displaying
+// as an inline query result.
+type InputLocationMessageContent struct {
+ Latitude float64 `json:"latitude"`
+ Longitude float64 `json:"longitude"`
+}
+
+// InputVenueMessageContent contains a venue for displaying
+// as an inline query result.
+type InputVenueMessageContent struct {
+ Latitude float64 `json:"latitude"`
+ Longitude float64 `json:"longitude"`
+ Title string `json:"title"`
+ Address string `json:"address"`
+ FoursquareID string `json:"foursquare_id"`
+}
+
+// InputContactMessageContent contains a contact for displaying
+// as an inline query result.
+type InputContactMessageContent struct {
+ PhoneNumber string `json:"phone_number"`
+ FirstName string `json:"first_name"`
+ LastName string `json:"last_name"`
+}
diff --git a/vendor/manifest b/vendor/manifest
index fd4a0969..4e5f5d11 100644
--- a/vendor/manifest
+++ b/vendor/manifest
@@ -60,6 +60,14 @@
"notests": true
},
{
+ "importpath": "github.com/go-telegram-bot-api/telegram-bot-api",
+ "repository": "https://github.com/go-telegram-bot-api/telegram-bot-api",
+ "vcs": "git",
+ "revision": "a7f48eb2dd301356942677e65bebe0c9aef07013",
+ "branch": "master",
+ "notests": true
+ },
+ {
"importpath": "github.com/gorilla/schema",
"repository": "https://github.com/gorilla/schema",
"vcs": "",
@@ -167,6 +175,14 @@
"notests": true
},
{
+ "importpath": "github.com/technoweenie/multipartstreamer",
+ "repository": "https://github.com/technoweenie/multipartstreamer",
+ "vcs": "git",
+ "revision": "a90a01d73ae432e2611d178c18367fbaa13e0154",
+ "branch": "master",
+ "notests": true
+ },
+ {
"importpath": "github.com/thoj/go-ircevent",
"repository": "https://github.com/thoj/go-ircevent",
"vcs": "git",