main
parent
89f3bdeeec
commit
ffc70d4648
14 changed files with 7948 additions and 0 deletions
@ -0,0 +1,4 @@ |
||||
.idea/ |
||||
coverage.out |
||||
tmp/ |
||||
book/ |
@ -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. |
@ -0,0 +1,121 @@ |
||||
# Golang bindings for the Telegram Bot API |
||||
|
||||
[](https://pkg.go.dev/github.com/go-telegram-bot-api/telegram-bot-api/v5) |
||||
[](https://github.com/go-telegram-bot-api/telegram-bot-api/actions/workflows/test.yml) |
||||
|
||||
All methods are fairly self-explanatory, and reading the [godoc](https://pkg.go.dev/github.com/go-telegram-bot-api/telegram-bot-api/v5) page should |
||||
explain everything. If something isn't clear, open an issue or submit |
||||
a pull request. |
||||
|
||||
There are more tutorials and high-level information on the website, [go-telegram-bot-api.dev](https://go-telegram-bot-api.dev). |
||||
|
||||
The scope of this project is just to provide a wrapper around the API |
||||
without any additional features. There are other projects for creating |
||||
something with plugins and command handlers without having to design |
||||
all that yourself. |
||||
|
||||
Join [the development group](https://telegram.me/go_telegram_bot_api) if |
||||
you want to ask questions or discuss development. |
||||
|
||||
## 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/v5`. |
||||
|
||||
This is a very simple bot that just displays any gotten updates, |
||||
then replies it to that chat. |
||||
|
||||
```go |
||||
package main |
||||
|
||||
import ( |
||||
"log" |
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" |
||||
) |
||||
|
||||
func main() { |
||||
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") |
||||
if err != nil { |
||||
log.Panic(err) |
||||
} |
||||
|
||||
bot.Debug = true |
||||
|
||||
log.Printf("Authorized on account %s", bot.Self.UserName) |
||||
|
||||
u := tgbotapi.NewUpdate(0) |
||||
u.Timeout = 60 |
||||
|
||||
updates := bot.GetUpdatesChan(u) |
||||
|
||||
for update := range updates { |
||||
if update.Message != nil { // If we got a message |
||||
log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text) |
||||
|
||||
msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text) |
||||
msg.ReplyToMessageID = update.Message.MessageID |
||||
|
||||
bot.Send(msg) |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
If you need to use webhooks (if you wish to run on Google App Engine), |
||||
you may use a slightly different method. |
||||
|
||||
```go |
||||
package main |
||||
|
||||
import ( |
||||
"log" |
||||
"net/http" |
||||
|
||||
"github.com/go-telegram-bot-api/telegram-bot-api/v5" |
||||
) |
||||
|
||||
func main() { |
||||
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
|
||||
bot.Debug = true |
||||
|
||||
log.Printf("Authorized on account %s", bot.Self.UserName) |
||||
|
||||
wh, _ := tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem") |
||||
|
||||
_, err = bot.SetWebhook(wh) |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
|
||||
info, err := bot.GetWebhookInfo() |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
|
||||
if info.LastErrorDate != 0 { |
||||
log.Printf("Telegram callback failed: %s", info.LastErrorMessage) |
||||
} |
||||
|
||||
updates := bot.ListenForWebhook("/" + bot.Token) |
||||
go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil) |
||||
|
||||
for update := range updates { |
||||
log.Printf("%+v\n", update) |
||||
} |
||||
} |
||||
``` |
||||
|
||||
If you need, you may generate a self-signed certificate, as this requires |
||||
HTTPS / TLS. The above example tells Telegram that this is your |
||||
certificate and that it should be trusted, even though it is not |
||||
properly signed. |
||||
|
||||
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3560 -subj "//O=Org\CN=Test" -nodes |
||||
|
||||
Now that [Let's Encrypt](https://letsencrypt.org) is available, |
||||
you may wish to generate your free TLS certificate there. |
@ -0,0 +1,9 @@ |
||||
[book] |
||||
authors = ["Syfaro"] |
||||
language = "en" |
||||
multilingual = false |
||||
src = "docs" |
||||
title = "Go Telegram Bot API" |
||||
|
||||
[output.html] |
||||
git-repository-url = "https://github.com/go-telegram-bot-api/telegram-bot-api" |
@ -0,0 +1,726 @@ |
||||
// Package tgbotapi has functions and types used for interacting with
|
||||
// the Telegram Bot API.
|
||||
package tgbotapi |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"fmt" |
||||
"io" |
||||
"io/ioutil" |
||||
"mime/multipart" |
||||
"net/http" |
||||
"net/url" |
||||
"strings" |
||||
"time" |
||||
) |
||||
|
||||
// HTTPClient is the type needed for the bot to perform HTTP requests.
|
||||
type HTTPClient interface { |
||||
Do(req *http.Request) (*http.Response, error) |
||||
} |
||||
|
||||
// BotAPI allows you to interact with the Telegram Bot API.
|
||||
type BotAPI struct { |
||||
Token string `json:"token"` |
||||
Debug bool `json:"debug"` |
||||
Buffer int `json:"buffer"` |
||||
|
||||
Self User `json:"-"` |
||||
Client HTTPClient `json:"-"` |
||||
shutdownChannel chan interface{} |
||||
|
||||
apiEndpoint string |
||||
} |
||||
|
||||
// NewBotAPI creates a new BotAPI instance.
|
||||
//
|
||||
// It requires a token, provided by @BotFather on Telegram.
|
||||
func NewBotAPI(token string) (*BotAPI, error) { |
||||
return NewBotAPIWithClient(token, APIEndpoint, &http.Client{}) |
||||
} |
||||
|
||||
// NewBotAPIWithAPIEndpoint creates a new BotAPI instance
|
||||
// and allows you to pass API endpoint.
|
||||
//
|
||||
// It requires a token, provided by @BotFather on Telegram and API endpoint.
|
||||
func NewBotAPIWithAPIEndpoint(token, apiEndpoint string) (*BotAPI, error) { |
||||
return NewBotAPIWithClient(token, apiEndpoint, &http.Client{}) |
||||
} |
||||
|
||||
// NewBotAPIWithClient creates a new BotAPI instance
|
||||
// and allows you to pass a http.Client.
|
||||
//
|
||||
// It requires a token, provided by @BotFather on Telegram and API endpoint.
|
||||
func NewBotAPIWithClient(token, apiEndpoint string, client HTTPClient) (*BotAPI, error) { |
||||
bot := &BotAPI{ |
||||
Token: token, |
||||
Client: client, |
||||
Buffer: 100, |
||||
shutdownChannel: make(chan interface{}), |
||||
|
||||
apiEndpoint: apiEndpoint, |
||||
} |
||||
|
||||
self, err := bot.GetMe() |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
bot.Self = self |
||||
|
||||
return bot, nil |
||||
} |
||||
|
||||
// SetAPIEndpoint changes the Telegram Bot API endpoint used by the instance.
|
||||
func (bot *BotAPI) SetAPIEndpoint(apiEndpoint string) { |
||||
bot.apiEndpoint = apiEndpoint |
||||
} |
||||
|
||||
func buildParams(in Params) url.Values { |
||||
if in == nil { |
||||
return url.Values{} |
||||
} |
||||
|
||||
out := url.Values{} |
||||
|
||||
for key, value := range in { |
||||
out.Set(key, value) |
||||
} |
||||
|
||||
return out |
||||
} |
||||
|
||||
// MakeRequest makes a request to a specific endpoint with our token.
|
||||
func (bot *BotAPI) MakeRequest(endpoint string, params Params) (*APIResponse, error) { |
||||
if bot.Debug { |
||||
log.Printf("Endpoint: %s, params: %v\n", endpoint, params) |
||||
} |
||||
|
||||
method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint) |
||||
|
||||
values := buildParams(params) |
||||
|
||||
req, err := http.NewRequest("POST", method, strings.NewReader(values.Encode())) |
||||
if err != nil { |
||||
return &APIResponse{}, err |
||||
} |
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
||||
|
||||
resp, err := bot.Client.Do(req) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer resp.Body.Close() |
||||
|
||||
var apiResp APIResponse |
||||
bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp) |
||||
if err != nil { |
||||
return &apiResp, err |
||||
} |
||||
|
||||
if bot.Debug { |
||||
log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes)) |
||||
} |
||||
|
||||
if !apiResp.Ok { |
||||
var parameters ResponseParameters |
||||
|
||||
if apiResp.Parameters != nil { |
||||
parameters = *apiResp.Parameters |
||||
} |
||||
|
||||
return &apiResp, &Error{ |
||||
Code: apiResp.ErrorCode, |
||||
Message: apiResp.Description, |
||||
ResponseParameters: parameters, |
||||
} |
||||
} |
||||
|
||||
return &apiResp, nil |
||||
} |
||||
|
||||
// decodeAPIResponse decode response and return slice of bytes if debug enabled.
|
||||
// If debug disabled, just decode http.Response.Body stream to APIResponse struct
|
||||
// for efficient memory usage
|
||||
func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse) ([]byte, error) { |
||||
if !bot.Debug { |
||||
dec := json.NewDecoder(responseBody) |
||||
err := dec.Decode(resp) |
||||
return nil, err |
||||
} |
||||
|
||||
// if debug, read response body
|
||||
data, err := ioutil.ReadAll(responseBody) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
err = json.Unmarshal(data, resp) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return data, nil |
||||
} |
||||
|
||||
// UploadFiles makes a request to the API with files.
|
||||
func (bot *BotAPI) UploadFiles(endpoint string, params Params, files []RequestFile) (*APIResponse, error) { |
||||
r, w := io.Pipe() |
||||
m := multipart.NewWriter(w) |
||||
|
||||
// This code modified from the very helpful @HirbodBehnam
|
||||
// https://github.com/go-telegram-bot-api/telegram-bot-api/issues/354#issuecomment-663856473
|
||||
go func() { |
||||
defer w.Close() |
||||
defer m.Close() |
||||
|
||||
for field, value := range params { |
||||
if err := m.WriteField(field, value); err != nil { |
||||
w.CloseWithError(err) |
||||
return |
||||
} |
||||
} |
||||
|
||||
for _, file := range files { |
||||
if file.Data.NeedsUpload() { |
||||
name, reader, err := file.Data.UploadData() |
||||
if err != nil { |
||||
w.CloseWithError(err) |
||||
return |
||||
} |
||||
|
||||
part, err := m.CreateFormFile(file.Name, name) |
||||
if err != nil { |
||||
w.CloseWithError(err) |
||||
return |
||||
} |
||||
|
||||
if _, err := io.Copy(part, reader); err != nil { |
||||
w.CloseWithError(err) |
||||
return |
||||
} |
||||
|
||||
if closer, ok := reader.(io.ReadCloser); ok { |
||||
if err = closer.Close(); err != nil { |
||||
w.CloseWithError(err) |
||||
return |
||||
} |
||||
} |
||||
} else { |
||||
value := file.Data.SendData() |
||||
|
||||
if err := m.WriteField(file.Name, value); err != nil { |
||||
w.CloseWithError(err) |
||||
return |
||||
} |
||||
} |
||||
} |
||||
}() |
||||
|
||||
if bot.Debug { |
||||
log.Printf("Endpoint: %s, params: %v, with %d files\n", endpoint, params, len(files)) |
||||
} |
||||
|
||||
method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint) |
||||
|
||||
req, err := http.NewRequest("POST", method, r) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
req.Header.Set("Content-Type", m.FormDataContentType()) |
||||
|
||||
resp, err := bot.Client.Do(req) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer resp.Body.Close() |
||||
|
||||
var apiResp APIResponse |
||||
bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp) |
||||
if err != nil { |
||||
return &apiResp, err |
||||
} |
||||
|
||||
if bot.Debug { |
||||
log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes)) |
||||
} |
||||
|
||||
if !apiResp.Ok { |
||||
var parameters ResponseParameters |
||||
|
||||
if apiResp.Parameters != nil { |
||||
parameters = *apiResp.Parameters |
||||
} |
||||
|
||||
return &apiResp, &Error{ |
||||
Message: apiResp.Description, |
||||
ResponseParameters: parameters, |
||||
} |
||||
} |
||||
|
||||
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 |
||||
err = json.Unmarshal(resp.Result, &user) |
||||
|
||||
return user, err |
||||
} |
||||
|
||||
// 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) |
||||
} |
||||
|
||||
func hasFilesNeedingUpload(files []RequestFile) bool { |
||||
for _, file := range files { |
||||
if file.Data.NeedsUpload() { |
||||
return true |
||||
} |
||||
} |
||||
|
||||
return false |
||||
} |
||||
|
||||
// Request sends a Chattable to Telegram, and returns the APIResponse.
|
||||
func (bot *BotAPI) Request(c Chattable) (*APIResponse, error) { |
||||
params, err := c.params() |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
if t, ok := c.(Fileable); ok { |
||||
files := t.files() |
||||
|
||||
// If we have files that need to be uploaded, we should delegate the
|
||||
// request to UploadFile.
|
||||
if hasFilesNeedingUpload(files) { |
||||
return bot.UploadFiles(t.method(), params, files) |
||||
} |
||||
|
||||
// However, if there are no files to be uploaded, there's likely things
|
||||
// that need to be turned into params instead.
|
||||
for _, file := range files { |
||||
params[file.Name] = file.Data.SendData() |
||||
} |
||||
} |
||||
|
||||
return bot.MakeRequest(c.method(), params) |
||||
} |
||||
|
||||
// Send will send a Chattable item to Telegram and provides the
|
||||
// returned Message.
|
||||
func (bot *BotAPI) Send(c Chattable) (Message, error) { |
||||
resp, err := bot.Request(c) |
||||
if err != nil { |
||||
return Message{}, err |
||||
} |
||||
|
||||
var message Message |
||||
err = json.Unmarshal(resp.Result, &message) |
||||
|
||||
return message, err |
||||
} |
||||
|
||||
// SendMediaGroup sends a media group and returns the resulting messages.
|
||||
func (bot *BotAPI) SendMediaGroup(config MediaGroupConfig) ([]Message, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
var messages []Message |
||||
err = json.Unmarshal(resp.Result, &messages) |
||||
|
||||
return messages, err |
||||
} |
||||
|
||||
// GetUserProfilePhotos gets a user's profile photos.
|
||||
//
|
||||
// It requires UserID.
|
||||
// Offset and Limit are optional.
|
||||
func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return UserProfilePhotos{}, err |
||||
} |
||||
|
||||
var profilePhotos UserProfilePhotos |
||||
err = json.Unmarshal(resp.Result, &profilePhotos) |
||||
|
||||
return profilePhotos, err |
||||
} |
||||
|
||||
// GetFile returns a File which can download a file from Telegram.
|
||||
//
|
||||
// Requires FileID.
|
||||
func (bot *BotAPI) GetFile(config FileConfig) (File, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return File{}, err |
||||
} |
||||
|
||||
var file File |
||||
err = json.Unmarshal(resp.Result, &file) |
||||
|
||||
return file, err |
||||
} |
||||
|
||||
// GetUpdates fetches updates.
|
||||
// If a WebHook is set, this will not return any data!
|
||||
//
|
||||
// Offset, Limit, Timeout, and AllowedUpdates 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) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return []Update{}, err |
||||
} |
||||
|
||||
var updates []Update |
||||
err = json.Unmarshal(resp.Result, &updates) |
||||
|
||||
return updates, err |
||||
} |
||||
|
||||
// GetWebhookInfo allows you to fetch information about a webhook and if
|
||||
// one currently is set, along with pending update count and error messages.
|
||||
func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) { |
||||
resp, err := bot.MakeRequest("getWebhookInfo", nil) |
||||
if err != nil { |
||||
return WebhookInfo{}, err |
||||
} |
||||
|
||||
var info WebhookInfo |
||||
err = json.Unmarshal(resp.Result, &info) |
||||
|
||||
return info, err |
||||
} |
||||
|
||||
// GetUpdatesChan starts and returns a channel for getting updates.
|
||||
func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) UpdatesChannel { |
||||
ch := make(chan Update, bot.Buffer) |
||||
|
||||
go func() { |
||||
for { |
||||
select { |
||||
case <-bot.shutdownChannel: |
||||
close(ch) |
||||
return |
||||
default: |
||||
} |
||||
|
||||
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 |
||||
ch <- update |
||||
} |
||||
} |
||||
} |
||||
}() |
||||
|
||||
return ch |
||||
} |
||||
|
||||
// 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.
|
||||
func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel { |
||||
ch := make(chan Update, bot.Buffer) |
||||
|
||||
http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { |
||||
update, err := bot.HandleUpdate(r) |
||||
if err != nil { |
||||
errMsg, _ := json.Marshal(map[string]string{"error": err.Error()}) |
||||
w.WriteHeader(http.StatusBadRequest) |
||||
w.Header().Set("Content-Type", "application/json") |
||||
_, _ = w.Write(errMsg) |
||||
return |
||||
} |
||||
|
||||
ch <- *update |
||||
}) |
||||
|
||||
return ch |
||||
} |
||||
|
||||
// ListenForWebhookRespReqFormat registers a http handler for a single incoming webhook.
|
||||
func (bot *BotAPI) ListenForWebhookRespReqFormat(w http.ResponseWriter, r *http.Request) UpdatesChannel { |
||||
ch := make(chan Update, bot.Buffer) |
||||
|
||||
func(w http.ResponseWriter, r *http.Request) { |
||||
update, err := bot.HandleUpdate(r) |
||||
if err != nil { |
||||
errMsg, _ := json.Marshal(map[string]string{"error": err.Error()}) |
||||
w.WriteHeader(http.StatusBadRequest) |
||||
w.Header().Set("Content-Type", "application/json") |
||||
_, _ = w.Write(errMsg) |
||||
return |
||||
} |
||||
|
||||
ch <- *update |
||||
close(ch) |
||||
}(w, r) |
||||
|
||||
return ch |
||||
} |
||||
|
||||
// HandleUpdate parses and returns update received via webhook
|
||||
func (bot *BotAPI) HandleUpdate(r *http.Request) (*Update, error) { |
||||
if r.Method != http.MethodPost { |
||||
err := errors.New("wrong HTTP method required POST") |
||||
return nil, err |
||||
} |
||||
|
||||
var update Update |
||||
err := json.NewDecoder(r.Body).Decode(&update) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return &update, nil |
||||
} |
||||
|
||||
// WriteToHTTPResponse writes the request to the HTTP ResponseWriter.
|
||||
//
|
||||
// It doesn't support uploading files.
|
||||
//
|
||||
// See https://core.telegram.org/bots/api#making-requests-when-getting-updates
|
||||
// for details.
|
||||
func WriteToHTTPResponse(w http.ResponseWriter, c Chattable) error { |
||||
params, err := c.params() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
if t, ok := c.(Fileable); ok { |
||||
if hasFilesNeedingUpload(t.files()) { |
||||
return errors.New("unable to use http response to upload files") |
||||
} |
||||
} |
||||
|
||||
values := buildParams(params) |
||||
values.Set("method", c.method()) |
||||
|
||||
w.Header().Set("Content-Type", "application/x-www-form-urlencoded") |
||||
_, err = w.Write([]byte(values.Encode())) |
||||
return err |
||||
} |
||||
|
||||
// GetChat gets information about a chat.
|
||||
func (bot *BotAPI) GetChat(config ChatInfoConfig) (Chat, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return Chat{}, err |
||||
} |
||||
|
||||
var chat Chat |
||||
err = json.Unmarshal(resp.Result, &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 ChatAdministratorsConfig) ([]ChatMember, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return []ChatMember{}, err |
||||
} |
||||
|
||||
var members []ChatMember |
||||
err = json.Unmarshal(resp.Result, &members) |
||||
|
||||
return members, err |
||||
} |
||||
|
||||
// GetChatMembersCount gets the number of users in a chat.
|
||||
func (bot *BotAPI) GetChatMembersCount(config ChatMemberCountConfig) (int, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return -1, err |
||||
} |
||||
|
||||
var count int |
||||
err = json.Unmarshal(resp.Result, &count) |
||||
|
||||
return count, err |
||||
} |
||||
|
||||
// GetChatMember gets a specific chat member.
|
||||
func (bot *BotAPI) GetChatMember(config GetChatMemberConfig) (ChatMember, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return ChatMember{}, err |
||||
} |
||||
|
||||
var member ChatMember |
||||
err = json.Unmarshal(resp.Result, &member) |
||||
|
||||
return member, err |
||||
} |
||||
|
||||
// GetGameHighScores allows you to get the high scores for a game.
|
||||
func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return []GameHighScore{}, err |
||||
} |
||||
|
||||
var highScores []GameHighScore |
||||
err = json.Unmarshal(resp.Result, &highScores) |
||||
|
||||
return highScores, err |
||||
} |
||||
|
||||
// GetInviteLink get InviteLink for a chat
|
||||
func (bot *BotAPI) GetInviteLink(config ChatInviteLinkConfig) (string, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
var inviteLink string |
||||
err = json.Unmarshal(resp.Result, &inviteLink) |
||||
|
||||
return inviteLink, err |
||||
} |
||||
|
||||
// GetStickerSet returns a StickerSet.
|
||||
func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return StickerSet{}, err |
||||
} |
||||
|
||||
var stickers StickerSet |
||||
err = json.Unmarshal(resp.Result, &stickers) |
||||
|
||||
return stickers, err |
||||
} |
||||
|
||||
// StopPoll stops a poll and returns the result.
|
||||
func (bot *BotAPI) StopPoll(config StopPollConfig) (Poll, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return Poll{}, err |
||||
} |
||||
|
||||
var poll Poll |
||||
err = json.Unmarshal(resp.Result, &poll) |
||||
|
||||
return poll, err |
||||
} |
||||
|
||||
// GetMyCommands gets the currently registered commands.
|
||||
func (bot *BotAPI) GetMyCommands() ([]BotCommand, error) { |
||||
return bot.GetMyCommandsWithConfig(GetMyCommandsConfig{}) |
||||
} |
||||
|
||||
// GetMyCommandsWithConfig gets the currently registered commands with a config.
|
||||
func (bot *BotAPI) GetMyCommandsWithConfig(config GetMyCommandsConfig) ([]BotCommand, error) { |
||||
resp, err := bot.Request(config) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
var commands []BotCommand |
||||
err = json.Unmarshal(resp.Result, &commands) |
||||
|
||||
return commands, err |
||||
} |
||||
|
||||
// CopyMessage copy messages of any kind. The method is analogous to the method
|
||||
// forwardMessage, but the copied message doesn't have a link to the original
|
||||
// message. Returns the MessageID of the sent message on success.
|
||||
func (bot *BotAPI) CopyMessage(config CopyMessageConfig) (MessageID, error) { |
||||
params, err := config.params() |
||||
if err != nil { |
||||
return MessageID{}, err |
||||
} |
||||
|
||||
resp, err := bot.MakeRequest(config.method(), params) |
||||
if err != nil { |
||||
return MessageID{}, err |
||||
} |
||||
|
||||
var messageID MessageID |
||||
err = json.Unmarshal(resp.Result, &messageID) |
||||
|
||||
return messageID, err |
||||
} |
||||
|
||||
// EscapeText takes an input text and escape Telegram markup symbols.
|
||||
// In this way we can send a text without being afraid of having to escape the characters manually.
|
||||
// Note that you don't have to include the formatting style in the input text, or it will be escaped too.
|
||||
// If there is an error, an empty string will be returned.
|
||||
//
|
||||
// parseMode is the text formatting mode (ModeMarkdown, ModeMarkdownV2 or ModeHTML)
|
||||
// text is the input string that will be escaped
|
||||
func EscapeText(parseMode string, text string) string { |
||||
var replacer *strings.Replacer |
||||
|
||||
if parseMode == ModeHTML { |
||||
replacer = strings.NewReplacer("<", "<", ">", ">", "&", "&") |
||||
} else if parseMode == ModeMarkdown { |
||||
replacer = strings.NewReplacer("_", "\\_", "*", "\\*", "`", "\\`", "[", "\\[") |
||||
} else if parseMode == ModeMarkdownV2 { |
||||
replacer = strings.NewReplacer( |
||||
"_", "\\_", "*", "\\*", "[", "\\[", "]", "\\]", "(", |
||||
"\\(", ")", "\\)", "~", "\\~", "`", "\\`", ">", "\\>", |
||||
"#", "\\#", "+", "\\+", "-", "\\-", "=", "\\=", "|", |
||||
"\\|", "{", "\\{", "}", "\\}", ".", "\\.", "!", "\\!", |
||||
) |
||||
} else { |
||||
return "" |
||||
} |
||||
|
||||
return replacer.Replace(text) |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,927 @@ |
||||
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, |
||||
} |
||||
} |
||||
|
||||
// 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
|
||||
// by username.
|
||||
//
|
||||
// username is the username of the channel, text is the message text,
|
||||
// and the username should be in the form of `@username`.
|
||||
func NewMessageToChannel(username string, text string) MessageConfig { |
||||
return MessageConfig{ |
||||
BaseChat: BaseChat{ |
||||
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, |
||||
} |
||||
} |
||||
|
||||
// NewCopyMessage creates a new copy message.
|
||||
//
|
||||
// chatID is where to send it, fromChatID is the source chat,
|
||||
// and messageID is the ID of the original message.
|
||||
func NewCopyMessage(chatID int64, fromChatID int64, messageID int) CopyMessageConfig { |
||||
return CopyMessageConfig{ |
||||
BaseChat: BaseChat{ChatID: chatID}, |
||||
FromChatID: fromChatID, |
||||
MessageID: messageID, |
||||
} |
||||
} |
||||
|
||||
// NewPhoto creates a new sendPhoto request.
|
||||
//
|
||||
// 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 NewPhoto(chatID int64, file RequestFileData) PhotoConfig { |
||||
return PhotoConfig{ |
||||
BaseFile: BaseFile{ |
||||
BaseChat: BaseChat{ChatID: chatID}, |
||||
File: file, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewPhotoToChannel creates a new photo uploader to send a photo to a channel.
|
||||
//
|
||||
// Note that you must send animated GIFs as a document.
|
||||
func NewPhotoToChannel(username string, file RequestFileData) PhotoConfig { |
||||
return PhotoConfig{ |
||||
BaseFile: BaseFile{ |
||||
BaseChat: BaseChat{ |
||||
ChannelUsername: username, |
||||
}, |
||||
File: file, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewAudio creates a new sendAudio request.
|
||||
func NewAudio(chatID int64, file RequestFileData) AudioConfig { |
||||
return AudioConfig{ |
||||
BaseFile: BaseFile{ |
||||
BaseChat: BaseChat{ChatID: chatID}, |
||||
File: file, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewDocument creates a new sendDocument request.
|
||||
func NewDocument(chatID int64, file RequestFileData) DocumentConfig { |
||||
return DocumentConfig{ |
||||
BaseFile: BaseFile{ |
||||
BaseChat: BaseChat{ChatID: chatID}, |
||||
File: file, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewSticker creates a new sendSticker request.
|
||||
func NewSticker(chatID int64, file RequestFileData) StickerConfig { |
||||
return StickerConfig{ |
||||
BaseFile: BaseFile{ |
||||
BaseChat: BaseChat{ChatID: chatID}, |
||||
File: file, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewVideo creates a new sendVideo request.
|
||||
func NewVideo(chatID int64, file RequestFileData) VideoConfig { |
||||
return VideoConfig{ |
||||
BaseFile: BaseFile{ |
||||
BaseChat: BaseChat{ChatID: chatID}, |
||||
File: file, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewAnimation creates a new sendAnimation request.
|
||||
func NewAnimation(chatID int64, file RequestFileData) AnimationConfig { |
||||
return AnimationConfig{ |
||||
BaseFile: BaseFile{ |
||||
BaseChat: BaseChat{ChatID: chatID}, |
||||
File: file, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewVideoNote creates a new sendVideoNote request.
|
||||
//
|
||||
// chatID is where to send it, file is a string path to the file,
|
||||
// FileReader, or FileBytes.
|
||||
func NewVideoNote(chatID int64, length int, file RequestFileData) VideoNoteConfig { |
||||
return VideoNoteConfig{ |
||||
BaseFile: BaseFile{ |
||||
BaseChat: BaseChat{ChatID: chatID}, |
||||
File: file, |
||||
}, |
||||
Length: length, |
||||
} |
||||
} |
||||
|
||||
// NewVoice creates a new sendVoice request.
|
||||
func NewVoice(chatID int64, file RequestFileData) VoiceConfig { |
||||
return VoiceConfig{ |
||||
BaseFile: BaseFile{ |
||||
BaseChat: BaseChat{ChatID: chatID}, |
||||
File: file, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// 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 RequestFileData) InputMediaPhoto { |
||||
return InputMediaPhoto{ |
||||
BaseInputMedia{ |
||||
Type: "photo", |
||||
Media: media, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewInputMediaVideo creates a new InputMediaVideo.
|
||||
func NewInputMediaVideo(media RequestFileData) InputMediaVideo { |
||||
return InputMediaVideo{ |
||||
BaseInputMedia: BaseInputMedia{ |
||||
Type: "video", |
||||
Media: media, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewInputMediaAnimation creates a new InputMediaAnimation.
|
||||
func NewInputMediaAnimation(media RequestFileData) InputMediaAnimation { |
||||
return InputMediaAnimation{ |
||||
BaseInputMedia: BaseInputMedia{ |
||||
Type: "animation", |
||||
Media: media, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewInputMediaAudio creates a new InputMediaAudio.
|
||||
func NewInputMediaAudio(media RequestFileData) InputMediaAudio { |
||||
return InputMediaAudio{ |
||||
BaseInputMedia: BaseInputMedia{ |
||||
Type: "audio", |
||||
Media: media, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewInputMediaDocument creates a new InputMediaDocument.
|
||||
func NewInputMediaDocument(media RequestFileData) InputMediaDocument { |
||||
return InputMediaDocument{ |
||||
BaseInputMedia: BaseInputMedia{ |
||||
Type: "document", |
||||
Media: media, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// 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 int64) 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, error) { |
||||
u, err := url.Parse(link) |
||||
|
||||
if err != nil { |
||||
return WebhookConfig{}, err |
||||
} |
||||
|
||||
return WebhookConfig{ |
||||
URL: u, |
||||
}, nil |
||||
} |
||||
|
||||
// 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 RequestFileData) (WebhookConfig, error) { |
||||
u, err := url.Parse(link) |
||||
|
||||
if err != nil { |
||||
return WebhookConfig{}, err |
||||
} |
||||
|
||||
return WebhookConfig{ |
||||
URL: u, |
||||
Certificate: file, |
||||
}, nil |
||||
} |
||||
|
||||
// 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", |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultArticleMarkdownV2 creates a new inline query article with MarkdownV2 parsing.
|
||||
func NewInlineQueryResultArticleMarkdownV2(id, title, messageText string) InlineQueryResultArticle { |
||||
return InlineQueryResultArticle{ |
||||
Type: "article", |
||||
ID: id, |
||||
Title: title, |
||||
InputMessageContent: InputTextMessageContent{ |
||||
Text: messageText, |
||||
ParseMode: "MarkdownV2", |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// 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, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultCachedGIF create a new inline query with cached photo.
|
||||
func NewInlineQueryResultCachedGIF(id, gifID string) InlineQueryResultCachedGIF { |
||||
return InlineQueryResultCachedGIF{ |
||||
Type: "gif", |
||||
ID: id, |
||||
GIFID: gifID, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultMPEG4GIF creates a new inline query MPEG4 GIF.
|
||||
func NewInlineQueryResultMPEG4GIF(id, url string) InlineQueryResultMPEG4GIF { |
||||
return InlineQueryResultMPEG4GIF{ |
||||
Type: "mpeg4_gif", |
||||
ID: id, |
||||
URL: url, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultCachedMPEG4GIF create a new inline query with cached MPEG4 GIF.
|
||||
func NewInlineQueryResultCachedMPEG4GIF(id, MPEG4GIFID string) InlineQueryResultCachedMPEG4GIF { |
||||
return InlineQueryResultCachedMPEG4GIF{ |
||||
Type: "mpeg4_gif", |
||||
ID: id, |
||||
MPEG4FileID: MPEG4GIFID, |
||||
} |
||||
} |
||||
|
||||
// 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, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultCachedPhoto create a new inline query with cached photo.
|
||||
func NewInlineQueryResultCachedPhoto(id, photoID string) InlineQueryResultCachedPhoto { |
||||
return InlineQueryResultCachedPhoto{ |
||||
Type: "photo", |
||||
ID: id, |
||||
PhotoID: photoID, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultVideo creates a new inline query video.
|
||||
func NewInlineQueryResultVideo(id, url string) InlineQueryResultVideo { |
||||
return InlineQueryResultVideo{ |
||||
Type: "video", |
||||
ID: id, |
||||
URL: url, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultCachedVideo create a new inline query with cached video.
|
||||
func NewInlineQueryResultCachedVideo(id, videoID, title string) InlineQueryResultCachedVideo { |
||||
return InlineQueryResultCachedVideo{ |
||||
Type: "video", |
||||
ID: id, |
||||
VideoID: videoID, |
||||
Title: title, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultCachedSticker create a new inline query with cached sticker.
|
||||
func NewInlineQueryResultCachedSticker(id, stickerID, title string) InlineQueryResultCachedSticker { |
||||
return InlineQueryResultCachedSticker{ |
||||
Type: "sticker", |
||||
ID: id, |
||||
StickerID: stickerID, |
||||
Title: title, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultAudio creates a new inline query audio.
|
||||
func NewInlineQueryResultAudio(id, url, title string) InlineQueryResultAudio { |
||||
return InlineQueryResultAudio{ |
||||
Type: "audio", |
||||
ID: id, |
||||
URL: url, |
||||
Title: title, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultCachedAudio create a new inline query with cached photo.
|
||||
func NewInlineQueryResultCachedAudio(id, audioID string) InlineQueryResultCachedAudio { |
||||
return InlineQueryResultCachedAudio{ |
||||
Type: "audio", |
||||
ID: id, |
||||
AudioID: audioID, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultVoice creates a new inline query voice.
|
||||
func NewInlineQueryResultVoice(id, url, title string) InlineQueryResultVoice { |
||||
return InlineQueryResultVoice{ |
||||
Type: "voice", |
||||
ID: id, |
||||
URL: url, |
||||
Title: title, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultCachedVoice create a new inline query with cached photo.
|
||||
func NewInlineQueryResultCachedVoice(id, voiceID, title string) InlineQueryResultCachedVoice { |
||||
return InlineQueryResultCachedVoice{ |
||||
Type: "voice", |
||||
ID: id, |
||||
VoiceID: voiceID, |
||||
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, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultCachedDocument create a new inline query with cached photo.
|
||||
func NewInlineQueryResultCachedDocument(id, documentID, title string) InlineQueryResultCachedDocument { |
||||
return InlineQueryResultCachedDocument{ |
||||
Type: "document", |
||||
ID: id, |
||||
DocumentID: documentID, |
||||
Title: title, |
||||
} |
||||
} |
||||
|
||||
// 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, |
||||
} |
||||
} |
||||
|
||||
// NewInlineQueryResultVenue creates a new inline query venue.
|
||||
func NewInlineQueryResultVenue(id, title, address string, latitude, longitude float64) InlineQueryResultVenue { |
||||
return InlineQueryResultVenue{ |
||||
Type: "venue", |
||||
ID: id, |
||||
Title: title, |
||||
Address: address, |
||||
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, |
||||
} |
||||
} |
||||
|
||||
// NewEditMessageTextAndMarkup allows you to edit the text and replymarkup of a message.
|
||||
func NewEditMessageTextAndMarkup(chatID int64, messageID int, text string, replyMarkup InlineKeyboardMarkup) EditMessageTextConfig { |
||||
return EditMessageTextConfig{ |
||||
BaseEdit: BaseEdit{ |
||||
ChatID: chatID, |
||||
MessageID: messageID, |
||||
ReplyMarkup: &replyMarkup, |
||||
}, |
||||
Text: text, |
||||
} |
||||
} |
||||
|
||||
// NewEditMessageCaption allows you to edit the caption of a message.
|
||||
func NewEditMessageCaption(chatID int64, messageID int, caption string) EditMessageCaptionConfig { |
||||
return EditMessageCaptionConfig{ |
||||
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, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewRemoveKeyboard hides the keyboard, with the option for being selective
|
||||
// or hiding for everyone.
|
||||
func NewRemoveKeyboard(selective bool) ReplyKeyboardRemove { |
||||
return ReplyKeyboardRemove{ |
||||
RemoveKeyboard: 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, |
||||
} |
||||
} |
||||
|
||||
// NewOneTimeReplyKeyboard creates a new one time keyboard.
|
||||
func NewOneTimeReplyKeyboard(rows ...[]KeyboardButton) ReplyKeyboardMarkup { |
||||
markup := NewReplyKeyboard(rows...) |
||||
markup.OneTimeKeyboard = true |
||||
return markup |
||||
} |
||||
|
||||
// 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, |
||||
} |
||||
} |
||||
|
||||
// NewInlineKeyboardButtonLoginURL creates an inline keyboard button with text
|
||||
// which goes to a LoginURL.
|
||||
func NewInlineKeyboardButtonLoginURL(text string, loginURL LoginURL) InlineKeyboardButton { |
||||
return InlineKeyboardButton{ |
||||
Text: text, |
||||
LoginURL: &loginURL, |
||||
} |
||||
} |
||||
|
||||
// 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, |
||||
} |
||||
} |
||||
|
||||
// NewInvoice creates a new Invoice request to the user.
|
||||
func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices []LabeledPrice) InvoiceConfig { |
||||
return InvoiceConfig{ |
||||
BaseChat: BaseChat{ChatID: chatID}, |
||||
Title: title, |
||||
Description: description, |
||||
Payload: payload, |
||||
ProviderToken: providerToken, |
||||
StartParameter: startParameter, |
||||
Currency: currency, |
||||
Prices: prices} |
||||
} |
||||
|
||||
// NewChatTitle allows you to update the title of a chat.
|
||||
func NewChatTitle(chatID int64, title string) SetChatTitleConfig { |
||||
return SetChatTitleConfig{ |
||||
ChatID: chatID, |
||||
Title: title, |
||||
} |
||||
} |
||||
|
||||
// NewChatDescription allows you to update the description of a chat.
|
||||
func NewChatDescription(chatID int64, description string) SetChatDescriptionConfig { |
||||
return SetChatDescriptionConfig{ |
||||
ChatID: chatID, |
||||
Description: description, |
||||
} |
||||
} |
||||
|
||||
// NewChatPhoto allows you to update the photo for a chat.
|
||||
func NewChatPhoto(chatID int64, photo RequestFileData) SetChatPhotoConfig { |
||||
return SetChatPhotoConfig{ |
||||
BaseFile: BaseFile{ |
||||
BaseChat: BaseChat{ |
||||
ChatID: chatID, |
||||
}, |
||||
File: photo, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewDeleteChatPhoto allows you to delete the photo for a chat.
|
||||
func NewDeleteChatPhoto(chatID int64) DeleteChatPhotoConfig { |
||||
return DeleteChatPhotoConfig{ |
||||
ChatID: chatID, |
||||
} |
||||
} |
||||
|
||||
// NewPoll allows you to create a new poll.
|
||||
func NewPoll(chatID int64, question string, options ...string) SendPollConfig { |
||||
return SendPollConfig{ |
||||
BaseChat: BaseChat{ |
||||
ChatID: chatID, |
||||
}, |
||||
Question: question, |
||||
Options: options, |
||||
IsAnonymous: true, // This is Telegram's default.
|
||||
} |
||||
} |
||||
|
||||
// NewStopPoll allows you to stop a poll.
|
||||
func NewStopPoll(chatID int64, messageID int) StopPollConfig { |
||||
return StopPollConfig{ |
||||
BaseEdit{ |
||||
ChatID: chatID, |
||||
MessageID: messageID, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewDice allows you to send a random dice roll.
|
||||
func NewDice(chatID int64) DiceConfig { |
||||
return DiceConfig{ |
||||
BaseChat: BaseChat{ |
||||
ChatID: chatID, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// NewDiceWithEmoji allows you to send a random roll of one of many types.
|
||||
//
|
||||
// Emoji may be 🎲 (1-6), 🎯 (1-6), or 🏀 (1-5).
|
||||
func NewDiceWithEmoji(chatID int64, emoji string) DiceConfig { |
||||
return DiceConfig{ |
||||
BaseChat: BaseChat{ |
||||
ChatID: chatID, |
||||
}, |
||||
Emoji: emoji, |
||||
} |
||||
} |
||||
|
||||
// NewBotCommandScopeDefault represents the default scope of bot commands.
|
||||
func NewBotCommandScopeDefault() BotCommandScope { |
||||
return BotCommandScope{Type: "default"} |
||||
} |
||||
|
||||
// NewBotCommandScopeAllPrivateChats represents the scope of bot commands,
|
||||
// covering all private chats.
|
||||
func NewBotCommandScopeAllPrivateChats() BotCommandScope { |
||||
return BotCommandScope{Type: "all_private_chats"} |
||||
} |
||||
|
||||
// NewBotCommandScopeAllGroupChats represents the scope of bot commands,
|
||||
// covering all group and supergroup chats.
|
||||
func NewBotCommandScopeAllGroupChats() BotCommandScope { |
||||
return BotCommandScope{Type: "all_group_chats"} |
||||
} |
||||
|
||||
// NewBotCommandScopeAllChatAdministrators represents the scope of bot commands,
|
||||
// covering all group and supergroup chat administrators.
|
||||
func NewBotCommandScopeAllChatAdministrators() BotCommandScope { |
||||
return BotCommandScope{Type: "all_chat_administrators"} |
||||
} |
||||
|
||||
// NewBotCommandScopeChat represents the scope of bot commands, covering a
|
||||
// specific chat.
|
||||
func NewBotCommandScopeChat(chatID int64) BotCommandScope { |
||||
return BotCommandScope{ |
||||
Type: "chat", |
||||
ChatID: chatID, |
||||
} |
||||
} |
||||
|
||||
// NewBotCommandScopeChatAdministrators represents the scope of bot commands,
|
||||
// covering all administrators of a specific group or supergroup chat.
|
||||
func NewBotCommandScopeChatAdministrators(chatID int64) BotCommandScope { |
||||
return BotCommandScope{ |
||||
Type: "chat_administrators", |
||||
ChatID: chatID, |
||||
} |
||||
} |
||||
|
||||
// NewBotCommandScopeChatMember represents the scope of bot commands, covering a
|
||||
// specific member of a group or supergroup chat.
|
||||
func NewBotCommandScopeChatMember(chatID, userID int64) BotCommandScope { |
||||
return BotCommandScope{ |
||||
Type: "chat_member", |
||||
ChatID: chatID, |
||||
UserID: userID, |
||||
} |
||||
} |
||||
|
||||
// NewGetMyCommandsWithScope allows you to set the registered commands for a
|
||||
// given scope.
|
||||
func NewGetMyCommandsWithScope(scope BotCommandScope) GetMyCommandsConfig { |
||||
return GetMyCommandsConfig{Scope: &scope} |
||||
} |
||||
|
||||
// NewGetMyCommandsWithScopeAndLanguage allows you to set the registered
|
||||
// commands for a given scope and language code.
|
||||
func NewGetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) GetMyCommandsConfig { |
||||
return GetMyCommandsConfig{Scope: &scope, LanguageCode: languageCode} |
||||
} |
||||
|
||||
// NewSetMyCommands allows you to set the registered commands.
|
||||
func NewSetMyCommands(commands ...BotCommand) SetMyCommandsConfig { |
||||
return SetMyCommandsConfig{Commands: commands} |
||||
} |
||||
|
||||
// NewSetMyCommandsWithScope allows you to set the registered commands for a given scope.
|
||||
func NewSetMyCommandsWithScope(scope BotCommandScope, commands ...BotCommand) SetMyCommandsConfig { |
||||
return SetMyCommandsConfig{Commands: commands, Scope: &scope} |
||||
} |
||||
|
||||
// NewSetMyCommandsWithScopeAndLanguage allows you to set the registered commands for a given scope
|
||||
// and language code.
|
||||
func NewSetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string, commands ...BotCommand) SetMyCommandsConfig { |
||||
return SetMyCommandsConfig{Commands: commands, Scope: &scope, LanguageCode: languageCode} |
||||
} |
||||
|
||||
// NewDeleteMyCommands allows you to delete the registered commands.
|
||||
func NewDeleteMyCommands() DeleteMyCommandsConfig { |
||||
return DeleteMyCommandsConfig{} |
||||
} |
||||
|
||||
// NewDeleteMyCommandsWithScope allows you to delete the registered commands for a given
|
||||
// scope.
|
||||
func NewDeleteMyCommandsWithScope(scope BotCommandScope) DeleteMyCommandsConfig { |
||||
return DeleteMyCommandsConfig{Scope: &scope} |
||||
} |
||||
|
||||
// NewDeleteMyCommandsWithScopeAndLanguage allows you to delete the registered commands for a given
|
||||
// scope and language code.
|
||||
func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig { |
||||
return DeleteMyCommandsConfig{Scope: &scope, LanguageCode: languageCode} |
||||
} |
@ -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 |
||||
} |
@ -0,0 +1,97 @@ |
||||
package tgbotapi |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"reflect" |
||||
"strconv" |
||||
) |
||||
|
||||
// Params represents a set of parameters that gets passed to a request.
|
||||
type Params map[string]string |
||||
|
||||
// AddNonEmpty adds a value if it not an empty string.
|
||||
func (p Params) AddNonEmpty(key, value string) { |
||||
if value != "" { |
||||
p[key] = value |
||||
} |
||||
} |
||||
|
||||
// AddNonZero adds a value if it is not zero.
|
||||
func (p Params) AddNonZero(key string, value int) { |
||||
if value != 0 { |
||||
p[key] = strconv.Itoa(value) |
||||
} |
||||
} |
||||
|
||||
// AddNonZero64 is the same as AddNonZero except uses an int64.
|
||||
func (p Params) AddNonZero64(key string, value int64) { |
||||
if value != 0 { |
||||
p[key] = strconv.FormatInt(value, 10) |
||||
} |
||||
} |
||||
|
||||
// AddBool adds a value of a bool if it is true.
|
||||
func (p Params) AddBool(key string, value bool) { |
||||
if value { |
||||
p[key] = strconv.FormatBool(value) |
||||
} |
||||
} |
||||
|
||||
// AddNonZeroFloat adds a floating point value that is not zero.
|
||||
func (p Params) AddNonZeroFloat(key string, value float64) { |
||||
if value != 0 { |
||||
p[key] = strconv.FormatFloat(value, 'f', 6, 64) |
||||
} |
||||
} |
||||
|
||||
// AddInterface adds an interface if it is not nil and can be JSON marshalled.
|
||||
func (p Params) AddInterface(key string, value interface{}) error { |
||||
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) { |
||||
return nil |
||||
} |
||||
|
||||
b, err := json.Marshal(value) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
p[key] = string(b) |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// AddFirstValid attempts to add the first item that is not a default value.
|
||||
//
|
||||
// For example, AddFirstValid(0, "", "test") would add "test".
|
||||
func (p Params) AddFirstValid(key string, args ...interface{}) error { |
||||
for _, arg := range args { |
||||
switch v := arg.(type) { |
||||
case int: |
||||
if v != 0 { |
||||
p[key] = strconv.Itoa(v) |
||||
return nil |
||||
} |
||||
case int64: |
||||
if v != 0 { |
||||
p[key] = strconv.FormatInt(v, 10) |
||||
return nil |
||||
} |
||||
case string: |
||||
if v != "" { |
||||
p[key] = v |
||||
return nil |
||||
} |
||||
case nil: |
||||
default: |
||||
b, err := json.Marshal(arg) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
p[key] = string(b) |
||||
return nil |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,317 @@ |
||||
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"` |
||||
|
||||
FileUniqueID string `json:"file_unique_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 a 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"` |
||||
} |
||||
) |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue