Merge master into develop.

bot-api-6.1
Syfaro 2018-10-08 03:13:50 -05:00
commit 23ed97b145
10 changed files with 687 additions and 45 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.idea/ .idea/
coverage.out coverage.out
tmp/

View File

@ -1,5 +1,6 @@
language: go language: go
go: go:
- 1.4 - '1.10'
- '1.11'
- tip - tip

View File

@ -3,10 +3,6 @@
[![GoDoc](https://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api?status.svg)](http://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api) [![GoDoc](https://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api?status.svg)](http://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api)
[![Travis](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api.svg)](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api) [![Travis](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api.svg)](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api)
All methods have been added, and all features should be available.
If you want a feature that hasn't been added yet or something is broken,
open an issue and I'll see what I can do.
All methods are fairly self explanatory, and reading the godoc page should All methods are fairly self explanatory, and reading the godoc page should
explain everything. If something isn't clear, open an issue or submit explain everything. If something isn't clear, open an issue or submit
a pull request. a pull request.
@ -21,6 +17,9 @@ you want to ask questions or discuss development.
## Example ## Example
First, ensure the library is installed and up to date by running
`go get -u github.com/go-telegram-bot-api/telegram-bot-api`.
This is a very simple bot that just displays any gotten updates, This is a very simple bot that just displays any gotten updates,
then replies it to that chat. then replies it to that chat.
@ -49,7 +48,7 @@ func main() {
updates, err := bot.GetUpdatesChan(u) updates, err := bot.GetUpdatesChan(u)
for update := range updates { for update := range updates {
if update.Message == nil { if update.Message == nil { // ignore any non-Message Updates
continue continue
} }
@ -63,6 +62,11 @@ func main() {
} }
``` ```
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 differen kinds of things.
It's a great place to get started on using keyboards, commands, or other
kinds of reply markup.
If you need to use webhooks (if you wish to run on Google App Engine), If you need to use webhooks (if you wish to run on Google App Engine),
you may use a slightly different method. you may use a slightly different method.
@ -98,7 +102,7 @@ func main() {
} }
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)

125
bot.go
View File

@ -9,7 +9,6 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -26,8 +25,9 @@ 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{}
} }
// NewBotAPI creates a new BotAPI instance. // NewBotAPI creates a new BotAPI instance.
@ -43,9 +43,10 @@ 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{}),
} }
self, err := bot.GetMe() self, err := bot.GetMe()
@ -394,6 +395,12 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
go func() { go func() {
for { for {
select {
case <-bot.shutdownChannel:
return
default:
}
updates, err := bot.GetUpdates(config) updates, err := bot.GetUpdates(config)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
@ -404,10 +411,7 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
} }
for _, update := range updates { for _, update := range updates {
if update.UpdateID >= config.Offset { ch <- update
config.Offset = update.UpdateID + 1
ch <- update
}
} }
} }
}() }()
@ -415,6 +419,14 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
return ch, nil return ch, nil
} }
// StopReceivingUpdates stops the go routine which receives updates
func (bot *BotAPI) StopReceivingUpdates() {
if bot.Debug {
log.Println("Stopping the update receiver routine...")
}
close(bot.shutdownChannel)
}
// ListenForWebhook registers a http handler for a webhook. // ListenForWebhook registers a http handler for a webhook.
func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel { func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
ch := make(chan Update, bot.Buffer) ch := make(chan Update, bot.Buffer)
@ -519,6 +531,99 @@ func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error)
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))
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))
}
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))
}
return bot.MakeRequest("promoteChatMember", v)
}
// GetGameHighScores allows you to get the high scores for a game. // GetGameHighScores allows you to get the high scores for a game.
func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) { func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) {
v, _ := config.values() v, _ := config.values()

View File

@ -476,16 +476,12 @@ func TestSetWebhookWithCert(t *testing.T) {
t.Fail() t.Fail()
} }
info, err := bot.GetWebhookInfo() _, err = bot.GetWebhookInfo()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if info.LastErrorDate != 0 {
t.Errorf("failed to set webhook: %s", info.LastErrorMessage)
}
bot.Request(tgbotapi.RemoveWebhookConfig{}) bot.Request(tgbotapi.RemoveWebhookConfig{})
} }
@ -529,6 +525,20 @@ func TestUpdatesChan(t *testing.T) {
} }
} }
func TestSendWithMediaGroup(t *testing.T) {
bot, _ := getBot(t)
cfg := tgbotapi.NewMediaGroup(ChatID, []interface{}{
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.Request(cfg)
if err != nil {
t.Error(err)
}
}
func ExampleNewBotAPI() { func ExampleNewBotAPI() {
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken")
if err != nil { if err != nil {

View File

@ -243,7 +243,8 @@ func (config ForwardConfig) method() string {
// PhotoConfig contains information about a SendPhoto request. // PhotoConfig contains information about a SendPhoto request.
type PhotoConfig struct { type PhotoConfig struct {
BaseFile BaseFile
Caption string Caption string
ParseMode string
} }
// Params returns a map[string]string representation of PhotoConfig. // Params returns a map[string]string representation of PhotoConfig.
@ -252,6 +253,9 @@ func (config PhotoConfig) params() (map[string]string, error) {
if config.Caption != "" { if config.Caption != "" {
params["caption"] = config.Caption params["caption"] = config.Caption
if config.ParseMode != "" {
params["parse_mode"] = config.ParseMode
}
} }
return params, nil return params, nil
@ -267,7 +271,11 @@ func (config PhotoConfig) values() (url.Values, error) {
v.Add(config.name(), config.FileID) v.Add(config.name(), config.FileID)
if config.Caption != "" { if config.Caption != "" {
v.Add("caption", config.Caption) v.Add("caption", config.Caption)
if config.ParseMode != "" {
v.Add("parse_mode", config.ParseMode)
}
} }
return v, nil return v, nil
} }
@ -285,6 +293,7 @@ func (config PhotoConfig) method() string {
type AudioConfig struct { type AudioConfig struct {
BaseFile BaseFile
Caption string Caption string
ParseMode string
Duration int Duration int
Performer string Performer string
Title string Title string
@ -310,6 +319,9 @@ func (config AudioConfig) values() (url.Values, error) {
} }
if config.Caption != "" { if config.Caption != "" {
v.Add("caption", config.Caption) v.Add("caption", config.Caption)
if config.ParseMode != "" {
v.Add("parse_mode", config.ParseMode)
}
} }
return v, nil return v, nil
@ -331,6 +343,9 @@ func (config AudioConfig) params() (map[string]string, error) {
} }
if config.Caption != "" { if config.Caption != "" {
params["caption"] = config.Caption params["caption"] = config.Caption
if config.ParseMode != "" {
params["parse_mode"] = config.ParseMode
}
} }
return params, nil return params, nil
@ -349,7 +364,8 @@ func (config AudioConfig) method() string {
// DocumentConfig contains information about a SendDocument request. // DocumentConfig contains information about a SendDocument request.
type DocumentConfig struct { type DocumentConfig struct {
BaseFile BaseFile
Caption string Caption string
ParseMode string
} }
// values returns a url.Values representation of DocumentConfig. // values returns a url.Values representation of DocumentConfig.
@ -362,6 +378,9 @@ func (config DocumentConfig) values() (url.Values, error) {
v.Add(config.name(), config.FileID) v.Add(config.name(), config.FileID)
if config.Caption != "" { if config.Caption != "" {
v.Add("caption", config.Caption) v.Add("caption", config.Caption)
if config.ParseMode != "" {
v.Add("parse_mode", config.ParseMode)
}
} }
return v, nil return v, nil
@ -373,6 +392,9 @@ func (config DocumentConfig) params() (map[string]string, error) {
if config.Caption != "" { if config.Caption != "" {
params["caption"] = config.Caption params["caption"] = config.Caption
if config.ParseMode != "" {
params["parse_mode"] = config.ParseMode
}
} }
return params, nil return params, nil
@ -425,8 +447,9 @@ func (config StickerConfig) method() string {
// VideoConfig contains information about a SendVideo request. // VideoConfig contains information about a SendVideo request.
type VideoConfig struct { type VideoConfig struct {
BaseFile BaseFile
Duration int Duration int
Caption string Caption string
ParseMode string
} }
// values returns a url.Values representation of VideoConfig. // values returns a url.Values representation of VideoConfig.
@ -442,6 +465,9 @@ func (config VideoConfig) values() (url.Values, error) {
} }
if config.Caption != "" { if config.Caption != "" {
v.Add("caption", config.Caption) v.Add("caption", config.Caption)
if config.ParseMode != "" {
v.Add("parse_mode", config.ParseMode)
}
} }
return v, nil return v, nil
@ -453,6 +479,9 @@ func (config VideoConfig) params() (map[string]string, error) {
if config.Caption != "" { if config.Caption != "" {
params["caption"] = config.Caption params["caption"] = config.Caption
if config.ParseMode != "" {
params["parse_mode"] = config.ParseMode
}
} }
return params, nil return params, nil
@ -468,6 +497,59 @@ func (config VideoConfig) method() string {
return "sendVideo" return "sendVideo"
} }
// AnimationConfig contains information about a SendAnimation request.
type AnimationConfig struct {
BaseFile
Duration int
Caption string
ParseMode string
}
// values returns a url.Values representation of AnimationConfig.
func (config AnimationConfig) values() (url.Values, error) {
v, err := config.BaseChat.values()
if err != nil {
return v, err
}
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)
if config.ParseMode != "" {
v.Add("parse_mode", config.ParseMode)
}
}
return v, nil
}
// params returns a map[string]string representation of AnimationConfig.
func (config AnimationConfig) params() (map[string]string, error) {
params, _ := config.BaseFile.params()
if config.Caption != "" {
params["caption"] = config.Caption
if config.ParseMode != "" {
params["parse_mode"] = config.ParseMode
}
}
return params, nil
}
// name returns the field name for the Animation.
func (config AnimationConfig) name() string {
return "animation"
}
// method returns Telegram API method name for sending Animation.
func (config AnimationConfig) method() string {
return "sendAnimation"
}
// VideoNoteConfig contains information about a SendVideoNote request. // VideoNoteConfig contains information about a SendVideoNote request.
type VideoNoteConfig struct { type VideoNoteConfig struct {
BaseFile BaseFile
@ -522,8 +604,9 @@ func (config VideoNoteConfig) method() string {
// VoiceConfig contains information about a SendVoice request. // VoiceConfig contains information about a SendVoice request.
type VoiceConfig struct { type VoiceConfig struct {
BaseFile BaseFile
Caption string Caption string
Duration int ParseMode string
Duration int
} }
// values returns a url.Values representation of VoiceConfig. // values returns a url.Values representation of VoiceConfig.
@ -539,6 +622,9 @@ func (config VoiceConfig) values() (url.Values, error) {
} }
if config.Caption != "" { if config.Caption != "" {
v.Add("caption", config.Caption) v.Add("caption", config.Caption)
if config.ParseMode != "" {
v.Add("parse_mode", config.ParseMode)
}
} }
return v, nil return v, nil
@ -553,6 +639,9 @@ func (config VoiceConfig) params() (map[string]string, error) {
} }
if config.Caption != "" { if config.Caption != "" {
params["caption"] = config.Caption params["caption"] = config.Caption
if config.ParseMode != "" {
params["parse_mode"] = config.ParseMode
}
} }
return params, nil return params, nil
@ -830,13 +919,17 @@ func (config EditMessageTextConfig) method() string {
// EditMessageCaptionConfig allows you to modify the caption of a message. // EditMessageCaptionConfig allows you to modify the caption of a message.
type EditMessageCaptionConfig struct { type EditMessageCaptionConfig struct {
BaseEdit BaseEdit
Caption string Caption string
ParseMode string
} }
func (config EditMessageCaptionConfig) values() (url.Values, error) { func (config EditMessageCaptionConfig) values() (url.Values, error) {
v, _ := config.BaseEdit.values() v, _ := config.BaseEdit.values()
v.Add("caption", config.Caption) v.Add("caption", config.Caption)
if config.ParseMode != "" {
v.Add("parse_mode", config.ParseMode)
}
return v, nil return v, nil
} }

View File

@ -1,7 +1,6 @@
package tgbotapi package tgbotapi
import ( import (
"log"
"net/url" "net/url"
) )
@ -14,11 +13,19 @@ func NewMessage(chatID int64, text string) MessageConfig {
ChatID: chatID, ChatID: chatID,
ReplyToMessageID: 0, ReplyToMessageID: 0,
}, },
Text: text, Text: text,
DisableWebPagePreview: false, DisableWebPagePreview: false,
} }
} }
// NewDeleteMessage creates a request to delete a message.
func NewDeleteMessage(chatID int64, messageID int) DeleteMessageConfig {
return DeleteMessageConfig{
ChatID: chatID,
MessageID: messageID,
}
}
// NewMessageToChannel creates a new Message that is sent to a channel // NewMessageToChannel creates a new Message that is sent to a channel
// by username. // by username.
// //
@ -194,6 +201,35 @@ func NewVideoShare(chatID int64, fileID string) VideoConfig {
} }
} }
// NewAnimationUpload creates a new animation uploader.
//
// chatID is where to send it, file is a string path to the file,
// FileReader, or FileBytes.
func NewAnimationUpload(chatID int64, file interface{}) AnimationConfig {
return AnimationConfig{
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
File: file,
UseExisting: false,
},
}
}
// NewAnimationShare shares an existing animation.
// You may use this to reshare an existing animation without reuploading it.
//
// chatID is where to send it, fileID is the ID of the animation
// already uploaded.
func NewAnimationShare(chatID int64, fileID string) AnimationConfig {
return AnimationConfig{
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
FileID: fileID,
UseExisting: true,
},
}
}
// NewVideoNoteUpload creates a new video note uploader. // NewVideoNoteUpload creates a new video note uploader.
// //
// chatID is where to send it, file is a string path to the file, // chatID is where to send it, file is a string path to the file,
@ -254,6 +290,31 @@ func NewVoiceShare(chatID int64, fileID string) VoiceConfig {
} }
} }
// NewMediaGroup creates a new media group. Files should be an array of
// two to ten InputMediaPhoto or InputMediaVideo.
func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig {
return MediaGroupConfig{
ChatID: chatID,
Media: files,
}
}
// NewInputMediaPhoto creates a new InputMediaPhoto.
func NewInputMediaPhoto(media string) InputMediaPhoto {
return InputMediaPhoto{
Type: "photo",
Media: media,
}
}
// NewInputMediaVideo creates a new InputMediaVideo.
func NewInputMediaVideo(media string) InputMediaVideo {
return InputMediaVideo{
Type: "video",
Media: media,
}
}
// NewContact allows you to send a shared contact. // NewContact allows you to send a shared contact.
func NewContact(chatID int64, phoneNumber, firstName string) ContactConfig { func NewContact(chatID int64, phoneNumber, firstName string) ContactConfig {
return ContactConfig{ return ContactConfig{

27
log.go 100644
View File

@ -0,0 +1,27 @@
package tgbotapi
import (
"errors"
stdlog "log"
"os"
)
// BotLogger is an interface that represents the required methods to log data.
//
// Instead of requiring the standard logger, we can just specify the methods we
// use and allow users to pass anything that implements these.
type BotLogger interface {
Println(v ...interface{})
Printf(format string, v ...interface{})
}
var log BotLogger = stdlog.New(os.Stderr, "", stdlog.LstdFlags)
// SetLogger specifies the logger that the package should use.
func SetLogger(logger BotLogger) error {
if logger == nil {
return errors.New("logger is nil")
}
log = logger
return nil
}

315
passport.go 100644
View File

@ -0,0 +1,315 @@
package tgbotapi
// PassportRequestInfoConfig allows you to request passport info
type PassportRequestInfoConfig struct {
BotID int `json:"bot_id"`
Scope *PassportScope `json:"scope"`
Nonce string `json:"nonce"`
PublicKey string `json:"public_key"`
}
// PassportScopeElement supports using one or one of several elements.
type PassportScopeElement interface {
ScopeType() string
}
// PassportScope is the requested scopes of data.
type PassportScope struct {
V int `json:"v"`
Data []PassportScopeElement `json:"data"`
}
// PassportScopeElementOneOfSeveral allows you to request any one of the
// requested documents.
type PassportScopeElementOneOfSeveral struct {
}
// ScopeType is the scope type.
func (eo *PassportScopeElementOneOfSeveral) ScopeType() string {
return "one_of"
}
// PassportScopeElementOne requires the specified element be provided.
type PassportScopeElementOne struct {
Type string `json:"type"` // One of “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport”, “address”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”, “phone_number”, “email”
Selfie bool `json:"selfie"`
Translation bool `json:"translation"`
NativeNames bool `json:"native_name"`
}
// ScopeType is the scope type.
func (eo *PassportScopeElementOne) ScopeType() string {
return "one"
}
type (
// PassportData contains information about Telegram Passport data shared with
// the bot by the user.
PassportData struct {
// Array with information about documents and other Telegram Passport
// elements that was shared with the bot
Data []EncryptedPassportElement `json:"data"`
// Encrypted credentials required to decrypt the data
Credentials *EncryptedCredentials `json:"credentials"`
}
// PassportFile represents a file uploaded to Telegram Passport. Currently all
// Telegram Passport files are in JPEG format when decrypted and don't exceed
// 10MB.
PassportFile struct {
// Unique identifier for this file
FileID string `json:"file_id"`
// File size
FileSize int `json:"file_size"`
// Unix time when the file was uploaded
FileDate int64 `json:"file_date"`
}
// EncryptedPassportElement contains information about documents or other
// Telegram Passport elements shared with the bot by the user.
EncryptedPassportElement struct {
// Element type.
Type string `json:"type"`
// Base64-encoded encrypted Telegram Passport element data provided by
// the user, available for "personal_details", "passport",
// "driver_license", "identity_card", "identity_passport" and "address"
// types. Can be decrypted and verified using the accompanying
// EncryptedCredentials.
Data string `json:"data,omitempty"`
// User's verified phone number, available only for "phone_number" type
PhoneNumber string `json:"phone_number,omitempty"`
// User's verified email address, available only for "email" type
Email string `json:"email,omitempty"`
// Array of encrypted files with documents provided by the user,
// available for "utility_bill", "bank_statement", "rental_agreement",
// "passport_registration" and "temporary_registration" types. Files can
// be decrypted and verified using the accompanying EncryptedCredentials.
Files []PassportFile `json:"files,omitempty"`
// Encrypted file with the front side of the document, provided by the
// user. Available for "passport", "driver_license", "identity_card" and
// "internal_passport". The file can be decrypted and verified using the
// accompanying EncryptedCredentials.
FrontSide *PassportFile `json:"front_side,omitempty"`
// Encrypted file with the reverse side of the document, provided by the
// user. Available for "driver_license" and "identity_card". The file can
// be decrypted and verified using the accompanying EncryptedCredentials.
ReverseSide *PassportFile `json:"reverse_side,omitempty"`
// Encrypted file with the selfie of the user holding a document,
// provided by the user; available for "passport", "driver_license",
// "identity_card" and "internal_passport". The file can be decrypted
// and verified using the accompanying EncryptedCredentials.
Selfie *PassportFile `json:"selfie,omitempty"`
}
// EncryptedCredentials contains data required for decrypting and
// authenticating EncryptedPassportElement. See the Telegram Passport
// Documentation for a complete description of the data decryption and
// authentication processes.
EncryptedCredentials struct {
// Base64-encoded encrypted JSON-serialized data with unique user's
// payload, data hashes and secrets required for EncryptedPassportElement
// decryption and authentication
Data string `json:"data"`
// Base64-encoded data hash for data authentication
Hash string `json:"hash"`
// Base64-encoded secret, encrypted with the bot's public RSA key,
// required for data decryption
Secret string `json:"secret"`
}
// PassportElementError represents an error in the Telegram Passport element
// which was submitted that should be resolved by the user.
PassportElementError interface{}
// PassportElementErrorDataField represents an issue in one of the data
// fields that was provided by the user. The error is considered resolved
// when the field's value changes.
PassportElementErrorDataField struct {
// Error source, must be data
Source string `json:"source"`
// The section of the user's Telegram Passport which has the error, one
// of "personal_details", "passport", "driver_license", "identity_card",
// "internal_passport", "address"
Type string `json:"type"`
// Name of the data field which has the error
FieldName string `json:"field_name"`
// Base64-encoded data hash
DataHash string `json:"data_hash"`
// Error message
Message string `json:"message"`
}
// PassportElementErrorFrontSide represents an issue with the front side of
// a document. The error is considered resolved when the file with the front
// side of the document changes.
PassportElementErrorFrontSide struct {
// Error source, must be front_side
Source string `json:"source"`
// The section of the user's Telegram Passport which has the issue, one
// of "passport", "driver_license", "identity_card", "internal_passport"
Type string `json:"type"`
// Base64-encoded hash of the file with the front side of the document
FileHash string `json:"file_hash"`
// Error message
Message string `json:"message"`
}
// PassportElementErrorReverseSide represents an issue with the reverse side
// of a document. The error is considered resolved when the file with reverse
// side of the document changes.
PassportElementErrorReverseSide struct {
// Error source, must be reverse_side
Source string `json:"source"`
// The section of the user's Telegram Passport which has the issue, one
// of "driver_license", "identity_card"
Type string `json:"type"`
// Base64-encoded hash of the file with the reverse side of the document
FileHash string `json:"file_hash"`
// Error message
Message string `json:"message"`
}
// PassportElementErrorSelfie represents an issue with the selfie with a
// document. The error is considered resolved when the file with the selfie
// changes.
PassportElementErrorSelfie struct {
// Error source, must be selfie
Source string `json:"source"`
// The section of the user's Telegram Passport which has the issue, one
// of "passport", "driver_license", "identity_card", "internal_passport"
Type string `json:"type"`
// Base64-encoded hash of the file with the selfie
FileHash string `json:"file_hash"`
// Error message
Message string `json:"message"`
}
// PassportElementErrorFile represents an issue with a document scan. The
// error is considered resolved when the file with the document scan changes.
PassportElementErrorFile struct {
// Error source, must be file
Source string `json:"source"`
// The section of the user's Telegram Passport which has the issue, one
// of "utility_bill", "bank_statement", "rental_agreement",
// "passport_registration", "temporary_registration"
Type string `json:"type"`
// Base64-encoded file hash
FileHash string `json:"file_hash"`
// Error message
Message string `json:"message"`
}
// PassportElementErrorFiles represents an issue with a list of scans. The
// error is considered resolved when the list of files containing the scans
// changes.
PassportElementErrorFiles struct {
// Error source, must be files
Source string `json:"source"`
// The section of the user's Telegram Passport which has the issue, one
// of "utility_bill", "bank_statement", "rental_agreement",
// "passport_registration", "temporary_registration"
Type string `json:"type"`
// List of base64-encoded file hashes
FileHashes []string `json:"file_hashes"`
// Error message
Message string `json:"message"`
}
// Credentials contains encrypted data.
Credentials struct {
Data SecureData `json:"secure_data"`
// Nonce the same nonce given in the request
Nonce string `json:"nonce"`
}
// SecureData is a map of the fields and their encrypted values.
SecureData map[string]*SecureValue
// PersonalDetails *SecureValue `json:"personal_details"`
// Passport *SecureValue `json:"passport"`
// InternalPassport *SecureValue `json:"internal_passport"`
// DriverLicense *SecureValue `json:"driver_license"`
// IdentityCard *SecureValue `json:"identity_card"`
// Address *SecureValue `json:"address"`
// UtilityBill *SecureValue `json:"utility_bill"`
// BankStatement *SecureValue `json:"bank_statement"`
// RentalAgreement *SecureValue `json:"rental_agreement"`
// PassportRegistration *SecureValue `json:"passport_registration"`
// TemporaryRegistration *SecureValue `json:"temporary_registration"`
// SecureValue contains encrypted values for a SecureData item.
SecureValue struct {
Data *DataCredentials `json:"data"`
FrontSide *FileCredentials `json:"front_side"`
ReverseSide *FileCredentials `json:"reverse_side"`
Selfie *FileCredentials `json:"selfie"`
Translation []*FileCredentials `json:"translation"`
Files []*FileCredentials `json:"files"`
}
// DataCredentials contains information required to decrypt data.
DataCredentials struct {
// DataHash checksum of encrypted data
DataHash string `json:"data_hash"`
// Secret of encrypted data
Secret string `json:"secret"`
}
// FileCredentials contains information required to decrypt files.
FileCredentials struct {
// FileHash checksum of encrypted data
FileHash string `json:"file_hash"`
// Secret of encrypted data
Secret string `json:"secret"`
}
// PersonalDetails https://core.telegram.org/passport#personaldetails
PersonalDetails struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
MiddleName string `json:"middle_name"`
BirthDate string `json:"birth_date"`
Gender string `json:"gender"`
CountryCode string `json:"country_code"`
ResidenceCountryCode string `json:"residence_country_code"`
FirstNameNative string `json:"first_name_native"`
LastNameNative string `json:"last_name_native"`
MiddleNameNative string `json:"middle_name_native"`
}
// IDDocumentData https://core.telegram.org/passport#iddocumentdata
IDDocumentData struct {
DocumentNumber string `json:"document_no"`
ExpiryDate string `json:"expiry_date"`
}
)

View File

@ -151,6 +151,7 @@ type Message struct {
CaptionEntities *[]MessageEntity `json:"caption_entities"` // optional CaptionEntities *[]MessageEntity `json:"caption_entities"` // optional
Audio *Audio `json:"audio"` // optional Audio *Audio `json:"audio"` // optional
Document *Document `json:"document"` // optional Document *Document `json:"document"` // optional
Animation *ChatAnimation `json:"animation"` // optional
Game *Game `json:"game"` // optional Game *Game `json:"game"` // optional
Photo *[]PhotoSize `json:"photo"` // optional Photo *[]PhotoSize `json:"photo"` // optional
Sticker *Sticker `json:"sticker"` // optional Sticker *Sticker `json:"sticker"` // optional
@ -174,6 +175,7 @@ type Message struct {
PinnedMessage *Message `json:"pinned_message"` // optional PinnedMessage *Message `json:"pinned_message"` // optional
Invoice *Invoice `json:"invoice"` // optional Invoice *Invoice `json:"invoice"` // optional
SuccessfulPayment *SuccessfulPayment `json:"successful_payment"` // optional SuccessfulPayment *SuccessfulPayment `json:"successful_payment"` // optional
PassportData *PassportData `json:"passport_data,omitempty"` // optional
} }
// Time converts the message timestamp into a Time. // Time converts the message timestamp into a Time.
@ -304,10 +306,29 @@ type Sticker struct {
// MaskPosition is the position of a mask. // MaskPosition is the position of a mask.
type MaskPosition struct { type MaskPosition struct {
Point string `json:"point"` Point string `json:"point"`
XShift float32 `json:"x_shift"` XShift float32 `json:"x_shift"`
YShift float32 `json:"y_shift"` YShift float32 `json:"y_shift"`
Scale float32 `json:"scale"` Scale float32 `json:"scale"`
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
SetName string `json:"set_name"` // optional
}
// ChatAnimation contains information about an animation.
type ChatAnimation struct {
FileID string `json:"file_id"`
Width int `json:"width"`
Height int `json:"height"`
Duration int `json:"duration"`
Thumbnail *PhotoSize `json:"thumb"` // optional
FileName string `json:"file_name"` // optional
MimeType string `json:"mime_type"` // optional
FileSize int `json:"file_size"` // optional
} }
// Video contains information about a video. // Video contains information about a video.
@ -803,21 +824,25 @@ type StickerSet struct {
// //
// Telegram recommends to use a file_id instead of uploading. // Telegram recommends to use a file_id instead of uploading.
type InputMediaPhoto struct { type InputMediaPhoto struct {
Type string `json:"type"` Type string `json:"type"`
Media string `json:"media"` Media string `json:"media"`
Caption string `json:"caption"` Caption string `json:"caption"`
ParseMode string `json:"parse_mode"`
} }
// InputMediaVideo is a video to send as part of a media group. // InputMediaVideo is a video to send as part of a media group.
// //
// Telegram recommends to use a file_id instead of uploading. // Telegram recommends to use a file_id instead of uploading.
type InputMediaVideo struct { type InputMediaVideo struct {
Type string `json:"type"` Type string `json:"type"`
Media string `json:"media"` Media string `json:"media"`
Caption string `json:"caption,omitempty"` // thumb intentionally missing as it is not currently compatible
Width int `json:"width,omitempty"` Caption string `json:"caption"`
Height int `json:"height,omitempty"` ParseMode string `json:"parse_mode"`
Duration int `json:"duration,omitempty"` Width int `json:"width"`
Height int `json:"height"`
Duration int `json:"duration"`
SupportsStreaming bool `json:"supports_streaming"`
} }
// Error is an error containing extra information returned by the Telegram API. // Error is an error containing extra information returned by the Telegram API.