Merge pull request #29 from zhulik/master
Major overhaul to make everything easier to usebot-api-6.1
commit
b034326d85
|
@ -0,0 +1,2 @@
|
|||
.idea/
|
||||
coverage.out
|
|
@ -0,0 +1,5 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- tip
|
16
README.md
16
README.md
|
@ -1,6 +1,7 @@
|
|||
# Golang bindings for the Telegram Bot API
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/Syfaro/telegram-bot-api?status.svg)](http://godoc.org/github.com/Syfaro/telegram-bot-api)
|
||||
[![Travis](https://travis-ci.org/Syfaro/telegram-bot-api.svg)](https://travis-ci.org/Syfaro/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.
|
||||
|
@ -34,18 +35,15 @@ func main() {
|
|||
u := tgbotapi.NewUpdate(0)
|
||||
u.Timeout = 60
|
||||
|
||||
err = bot.UpdatesChan(u)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
updates, err := bot.GetUpdatesChan(u)
|
||||
|
||||
for update := range bot.Updates {
|
||||
for update := range updates {
|
||||
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.SendMessage(msg)
|
||||
bot.Send(msg)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -76,10 +74,10 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
bot.ListenForWebhook("/"+bot.Token)
|
||||
updates, _ := bot.ListenForWebhook("/" + bot.Token)
|
||||
go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil)
|
||||
|
||||
for update := range bot.Updates {
|
||||
for update := range updates {
|
||||
log.Printf("%+v\n", update)
|
||||
}
|
||||
}
|
||||
|
@ -87,4 +85,4 @@ func main() {
|
|||
|
||||
If you need, you may generate a self signed certficate, as this requires HTTPS / TLS. The above example tells Telegram that this is your certificate and that it should be trusted, even though it is not properly signed.
|
||||
|
||||
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3560 -subj -nodes
|
||||
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3560 -subj "//O=Org\CN=Test" -nodes
|
||||
|
|
416
bot.go
416
bot.go
|
@ -2,7 +2,19 @@
|
|||
package tgbotapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/technoweenie/multipartstreamer"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BotAPI has methods for interacting with all of Telegram's Bot API endpoints.
|
||||
|
@ -10,7 +22,6 @@ type BotAPI struct {
|
|||
Token string `json:"token"`
|
||||
Debug bool `json:"debug"`
|
||||
Self User `json:"-"`
|
||||
Updates chan Update `json:"-"`
|
||||
Client *http.Client `json:"-"`
|
||||
}
|
||||
|
||||
|
@ -37,3 +48,406 @@ func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
|
|||
|
||||
return bot, nil
|
||||
}
|
||||
|
||||
// MakeRequest makes a request to a specific endpoint with our token.
|
||||
// All requests are POSTs because Telegram doesn't care, and it's easier.
|
||||
func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
|
||||
resp, err := bot.Client.PostForm(fmt.Sprintf(APIEndpoint, bot.Token, endpoint), params)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
return APIResponse{}, errors.New(APIForbidden)
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
|
||||
if bot.Debug {
|
||||
log.Println(endpoint, string(bytes))
|
||||
}
|
||||
|
||||
var apiResp APIResponse
|
||||
json.Unmarshal(bytes, &apiResp)
|
||||
|
||||
if !apiResp.Ok {
|
||||
return APIResponse{}, errors.New(apiResp.Description)
|
||||
}
|
||||
|
||||
return apiResp, nil
|
||||
}
|
||||
|
||||
func (bot *BotAPI) makeMessageRequest(endpoint string, params url.Values) (Message, error) {
|
||||
resp, err := bot.MakeRequest(endpoint, params)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
var message Message
|
||||
json.Unmarshal(resp.Result, &message)
|
||||
|
||||
bot.debugLog(endpoint, params, message)
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
// UploadFile makes a request to the API with a file.
|
||||
//
|
||||
// Requires the parameter to hold the file not be in the params.
|
||||
// File should be a string to a file path, a FileBytes struct, or a FileReader struct.
|
||||
func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
|
||||
ms := multipartstreamer.New()
|
||||
ms.WriteFields(params)
|
||||
|
||||
switch f := file.(type) {
|
||||
case string:
|
||||
fileHandle, err := os.Open(f)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
defer fileHandle.Close()
|
||||
|
||||
fi, err := os.Stat(f)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
|
||||
ms.WriteReader(fieldname, fileHandle.Name(), fi.Size(), fileHandle)
|
||||
case FileBytes:
|
||||
buf := bytes.NewBuffer(f.Bytes)
|
||||
ms.WriteReader(fieldname, f.Name, int64(len(f.Bytes)), buf)
|
||||
case FileReader:
|
||||
if f.Size == -1 {
|
||||
data, err := ioutil.ReadAll(f.Reader)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
buf := bytes.NewBuffer(data)
|
||||
|
||||
ms.WriteReader(fieldname, f.Name, int64(len(data)), buf)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
ms.WriteReader(fieldname, f.Name, f.Size, f.Reader)
|
||||
default:
|
||||
return APIResponse{}, errors.New("bad file type")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", fmt.Sprintf(APIEndpoint, bot.Token, endpoint), nil)
|
||||
ms.SetupRequest(req)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
|
||||
res, err := bot.Client.Do(req)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
bytes, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
|
||||
if bot.Debug {
|
||||
log.Println(string(bytes[:]))
|
||||
}
|
||||
|
||||
var apiResp APIResponse
|
||||
json.Unmarshal(bytes, &apiResp)
|
||||
|
||||
if !apiResp.Ok {
|
||||
return APIResponse{}, errors.New(apiResp.Description)
|
||||
}
|
||||
|
||||
return apiResp, nil
|
||||
}
|
||||
|
||||
// GetFileDirectURL returns direct URL to file
|
||||
//
|
||||
// Requires 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.
|
||||
//
|
||||
// There are no parameters for this method.
|
||||
func (bot *BotAPI) GetMe() (User, error) {
|
||||
resp, err := bot.MakeRequest("getMe", nil)
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
|
||||
var user User
|
||||
json.Unmarshal(resp.Result, &user)
|
||||
|
||||
if bot.Debug {
|
||||
log.Printf("getMe: %+v\n", user)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// IsMessageToMe returns true if message directed to this bot
|
||||
//
|
||||
// Requires message
|
||||
func (bot *BotAPI) IsMessageToMe(message Message) bool {
|
||||
return strings.Contains(message.Text, "@"+bot.Self.UserName)
|
||||
}
|
||||
|
||||
// Send will send event(Message, Photo, Audio, ChatAction, anything) to Telegram
|
||||
//
|
||||
// Requires Chattable
|
||||
func (bot *BotAPI) Send(c Chattable) (Message, error) {
|
||||
switch c.(type) {
|
||||
case Fileable:
|
||||
return bot.sendFile(c.(Fileable))
|
||||
default:
|
||||
return bot.sendChattable(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) {
|
||||
if bot.Debug {
|
||||
log.Printf("%s req : %+v\n", context, v)
|
||||
log.Printf("%s resp: %+v\n", context, message)
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) {
|
||||
v, err := config.Values()
|
||||
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
message, err := bot.makeMessageRequest(method, v)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) {
|
||||
params, err := config.Params()
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
file := config.GetFile()
|
||||
|
||||
resp, err := bot.UploadFile(method, params, config.Name(), file)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
var message Message
|
||||
json.Unmarshal(resp.Result, &message)
|
||||
|
||||
if bot.Debug {
|
||||
log.Printf("%s resp: %+v\n", method, message)
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
func (bot *BotAPI) sendFile(config Fileable) (Message, error) {
|
||||
if config.UseExistingFile() {
|
||||
return bot.sendExisting(config.Method(), config)
|
||||
}
|
||||
|
||||
return bot.uploadAndSend(config.Method(), config)
|
||||
}
|
||||
|
||||
func (bot *BotAPI) sendChattable(config Chattable) (Message, error) {
|
||||
v, err := config.Values()
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
message, err := bot.makeMessageRequest(config.Method(), v)
|
||||
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
// GetUserProfilePhotos gets a user's profile photos.
|
||||
//
|
||||
// Requires UserID.
|
||||
// Offset and Limit are optional.
|
||||
func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
|
||||
v := url.Values{}
|
||||
v.Add("user_id", strconv.Itoa(config.UserID))
|
||||
if config.Offset != 0 {
|
||||
v.Add("offset", strconv.Itoa(config.Offset))
|
||||
}
|
||||
if config.Limit != 0 {
|
||||
v.Add("limit", strconv.Itoa(config.Limit))
|
||||
}
|
||||
|
||||
resp, err := bot.MakeRequest("getUserProfilePhotos", v)
|
||||
if err != nil {
|
||||
return UserProfilePhotos{}, err
|
||||
}
|
||||
|
||||
var profilePhotos UserProfilePhotos
|
||||
json.Unmarshal(resp.Result, &profilePhotos)
|
||||
|
||||
bot.debugLog("GetUserProfilePhoto", v, profilePhotos)
|
||||
|
||||
return profilePhotos, nil
|
||||
}
|
||||
|
||||
// GetFile returns a file_id required to download a file.
|
||||
//
|
||||
// Requires FileID.
|
||||
func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
|
||||
v := url.Values{}
|
||||
v.Add("file_id", config.FileID)
|
||||
|
||||
resp, err := bot.MakeRequest("getFile", v)
|
||||
if err != nil {
|
||||
return File{}, err
|
||||
}
|
||||
|
||||
var file File
|
||||
json.Unmarshal(resp.Result, &file)
|
||||
|
||||
bot.debugLog("GetFile", v, file)
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// GetUpdates fetches updates.
|
||||
// If a WebHook is set, this will not return any data!
|
||||
//
|
||||
// Offset, Limit, and Timeout are optional.
|
||||
// To not get old items, set Offset to one higher than the previous item.
|
||||
// Set Timeout to a large number to reduce requests and get responses instantly.
|
||||
func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
|
||||
v := url.Values{}
|
||||
if config.Offset > 0 {
|
||||
v.Add("offset", strconv.Itoa(config.Offset))
|
||||
}
|
||||
if config.Limit > 0 {
|
||||
v.Add("limit", strconv.Itoa(config.Limit))
|
||||
}
|
||||
if config.Timeout > 0 {
|
||||
v.Add("timeout", strconv.Itoa(config.Timeout))
|
||||
}
|
||||
|
||||
resp, err := bot.MakeRequest("getUpdates", v)
|
||||
if err != nil {
|
||||
return []Update{}, err
|
||||
}
|
||||
|
||||
var updates []Update
|
||||
json.Unmarshal(resp.Result, &updates)
|
||||
|
||||
if bot.Debug {
|
||||
log.Printf("getUpdates: %+v\n", updates)
|
||||
}
|
||||
|
||||
return updates, nil
|
||||
}
|
||||
|
||||
// RemoveWebhook removes webhook
|
||||
//
|
||||
// There are no parameters for this method.
|
||||
func (bot *BotAPI) RemoveWebhook() (APIResponse, error) {
|
||||
return bot.MakeRequest("setWebhook", url.Values{})
|
||||
}
|
||||
|
||||
// SetWebhook sets a webhook.
|
||||
// If this is set, GetUpdates will not get any data!
|
||||
//
|
||||
// Requires URL OR to set Clear to true.
|
||||
func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
|
||||
if config.Certificate == nil {
|
||||
v := url.Values{}
|
||||
v.Add("url", config.URL.String())
|
||||
|
||||
return bot.MakeRequest("setWebhook", v)
|
||||
}
|
||||
|
||||
params := make(map[string]string)
|
||||
params["url"] = config.URL.String()
|
||||
|
||||
resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
|
||||
var apiResp APIResponse
|
||||
json.Unmarshal(resp.Result, &apiResp)
|
||||
|
||||
if bot.Debug {
|
||||
log.Printf("setWebhook resp: %+v\n", apiResp)
|
||||
}
|
||||
|
||||
return apiResp, nil
|
||||
}
|
||||
|
||||
// GetUpdatesChan starts and returns a channel for getting updates.
|
||||
//
|
||||
// Requires UpdateConfig
|
||||
func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (<-chan Update, error) {
|
||||
updatesChan := make(chan Update, 100)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
updates, err := bot.GetUpdates(config)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
log.Println("Failed to get updates, retrying in 3 seconds...")
|
||||
time.Sleep(time.Second * 3)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
for _, update := range updates {
|
||||
if update.UpdateID >= config.Offset {
|
||||
config.Offset = update.UpdateID + 1
|
||||
updatesChan <- update
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return updatesChan, nil
|
||||
}
|
||||
|
||||
// ListenForWebhook registers a http handler for a webhook.
|
||||
func (bot *BotAPI) ListenForWebhook(pattern string) (<-chan Update, http.Handler) {
|
||||
updatesChan := make(chan Update, 100)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
bytes, _ := ioutil.ReadAll(r.Body)
|
||||
|
||||
var update Update
|
||||
json.Unmarshal(bytes, &update)
|
||||
|
||||
updatesChan <- update
|
||||
})
|
||||
|
||||
http.HandleFunc(pattern, handler)
|
||||
|
||||
return updatesChan, handler
|
||||
}
|
||||
|
|
422
bot_test.go
422
bot_test.go
|
@ -1,63 +1,406 @@
|
|||
package tgbotapi_test
|
||||
|
||||
import (
|
||||
"github.com/syfaro/telegram-bot-api"
|
||||
"github.com/Syfaro/telegram-bot-api"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
botToken := os.Getenv("TELEGRAM_API_TOKEN")
|
||||
const (
|
||||
TestToken = "153667468:AAHlSHlMqSt1f_uFmVRJbm5gntu2HI4WW8I"
|
||||
ChatID = 76918703
|
||||
ReplyToMessageID = 35
|
||||
ExistingPhotoFileID = "AgADAgADw6cxG4zHKAkr42N7RwEN3IFShCoABHQwXEtVks4EH2wBAAEC"
|
||||
ExistingDocumentFileID = "BQADAgADOQADjMcoCcioX1GrDvp3Ag"
|
||||
ExistingAudioFileID = "BQADAgADRgADjMcoCdXg3lSIN49lAg"
|
||||
ExistingVoiceFileID = "AwADAgADWQADjMcoCeul6r_q52IyAg"
|
||||
ExistingVideoFileID = "BAADAgADZgADjMcoCav432kYe0FRAg"
|
||||
ExistingStickerFileID = "BQADAgADcwADjMcoCbdl-6eB--YPAg"
|
||||
)
|
||||
|
||||
if botToken == "" {
|
||||
log.Panic("You must provide a TELEGRAM_API_TOKEN env variable to test!")
|
||||
func getBot(t *testing.T) (*tgbotapi.BotAPI, error) {
|
||||
bot, err := tgbotapi.NewBotAPI(TestToken)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
os.Exit(m.Run())
|
||||
return bot, err
|
||||
}
|
||||
|
||||
func TestNewBotAPI_notoken(t *testing.T) {
|
||||
_, err := tgbotapi.NewBotAPI("")
|
||||
|
||||
if err.Error() != tgbotapi.APIForbidden {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewBotAPI_token(t *testing.T) {
|
||||
_, err := tgbotapi.NewBotAPI(os.Getenv("TELEGRAM_API_TOKEN"))
|
||||
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUpdates(t *testing.T) {
|
||||
bot, err := tgbotapi.NewBotAPI(os.Getenv("TELEGRAM_API_TOKEN"))
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
bot, _ := getBot(t)
|
||||
|
||||
u := tgbotapi.NewUpdate(0)
|
||||
|
||||
_, err = bot.GetUpdates(u)
|
||||
_, err := bot.GetUpdates(u)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendMessage(t *testing.T) {
|
||||
bot, err := tgbotapi.NewBotAPI(os.Getenv("TELEGRAM_API_TOKEN"))
|
||||
func TestSendWithMessage(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api")
|
||||
msg.ParseMode = "markdown"
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
msg := tgbotapi.NewMessage(36529758, "A test message from the test library in telegram-bot-api")
|
||||
bot.SendMessage(msg)
|
||||
func TestSendWithMessageReply(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api")
|
||||
msg.ReplyToMessageID = ReplyToMessageID
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithMessageForward(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewForward(ChatID, ChatID, ReplyToMessageID)
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithNewPhoto(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewPhotoUpload(ChatID, "tests/image.jpg")
|
||||
msg.Caption = "Test"
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithNewPhotoWithFileBytes(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
data, _ := ioutil.ReadFile("tests/image.jpg")
|
||||
b := tgbotapi.FileBytes{Name: "image.jpg", Bytes: data}
|
||||
|
||||
msg := tgbotapi.NewPhotoUpload(ChatID, b)
|
||||
msg.Caption = "Test"
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithNewPhotoWithFileReader(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
f, _ := os.Open("tests/image.jpg")
|
||||
reader := tgbotapi.FileReader{Name: "image.jpg", Reader: f, Size: -1}
|
||||
|
||||
msg := tgbotapi.NewPhotoUpload(ChatID, reader)
|
||||
msg.Caption = "Test"
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithNewPhotoReply(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewPhotoUpload(ChatID, "tests/image.jpg")
|
||||
msg.ReplyToMessageID = ReplyToMessageID
|
||||
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithExistingPhoto(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewPhotoShare(ChatID, ExistingPhotoFileID)
|
||||
msg.Caption = "Test"
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithNewDocument(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewDocumentUpload(ChatID, "tests/image.jpg")
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithExistingDocument(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewDocumentShare(ChatID, ExistingDocumentFileID)
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithNewAudio(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewAudioUpload(ChatID, "tests/audio.mp3")
|
||||
msg.Title = "TEST"
|
||||
msg.Duration = 10
|
||||
msg.Performer = "TEST"
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithExistingAudio(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewAudioShare(ChatID, ExistingAudioFileID)
|
||||
msg.Title = "TEST"
|
||||
msg.Duration = 10
|
||||
msg.Performer = "TEST"
|
||||
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithNewVoice(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewVoiceUpload(ChatID, "tests/voice.ogg")
|
||||
msg.Duration = 10
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithExistingVoice(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewVoiceShare(ChatID, ExistingVoiceFileID)
|
||||
msg.Duration = 10
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithLocation(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
_, err := bot.Send(tgbotapi.NewLocation(ChatID, 40, 40))
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithNewVideo(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewVideoUpload(ChatID, "tests/video.mp4")
|
||||
msg.Duration = 10
|
||||
msg.Caption = "TEST"
|
||||
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithExistingVideo(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewVideoShare(ChatID, ExistingVideoFileID)
|
||||
msg.Duration = 10
|
||||
msg.Caption = "TEST"
|
||||
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithNewSticker(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewStickerUpload(ChatID, "tests/image.jpg")
|
||||
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithExistingSticker(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewStickerShare(ChatID, ExistingStickerFileID)
|
||||
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithNewStickerAndKeyboardHide(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewStickerUpload(ChatID, "tests/image.jpg")
|
||||
msg.ReplyMarkup = tgbotapi.ReplyKeyboardHide{true, false}
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendWithExistingStickerAndKeyboardHide(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
msg := tgbotapi.NewStickerShare(ChatID, ExistingStickerFileID)
|
||||
msg.ReplyMarkup = tgbotapi.ReplyKeyboardHide{true, false}
|
||||
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFile(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
file := tgbotapi.FileConfig{ExistingPhotoFileID}
|
||||
|
||||
_, err := bot.GetFile(file)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendChatConfig(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
_, err := bot.Send(tgbotapi.NewChatAction(ChatID, tgbotapi.ChatTyping))
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserProfilePhotos(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
_, err := bot.GetUserProfilePhotos(tgbotapi.NewUserProfilePhotos(ChatID))
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestListenForWebhook(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
_, handler := bot.ListenForWebhook("/")
|
||||
|
||||
req, _ := http.NewRequest("GET", "", strings.NewReader("{}"))
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Home page didn't return %v", http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetWebhookWithCert(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
bot.RemoveWebhook()
|
||||
|
||||
wh := tgbotapi.NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, "tests/cert.pem")
|
||||
_, err := bot.SetWebhook(wh)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
bot.RemoveWebhook()
|
||||
}
|
||||
|
||||
func TestSetWebhookWithoutCert(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
bot.RemoveWebhook()
|
||||
|
||||
wh := tgbotapi.NewWebhook("https://example.com/tgbotapi-test/" + bot.Token)
|
||||
_, err := bot.SetWebhook(wh)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
bot.RemoveWebhook()
|
||||
}
|
||||
|
||||
func TestUpdatesChan(t *testing.T) {
|
||||
bot, _ := getBot(t)
|
||||
|
||||
var ucfg tgbotapi.UpdateConfig = tgbotapi.NewUpdate(0)
|
||||
ucfg.Timeout = 60
|
||||
_, err := bot.GetUpdatesChan(ucfg)
|
||||
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleNewBotAPI() {
|
||||
|
@ -73,14 +416,37 @@ func ExampleNewBotAPI() {
|
|||
u := tgbotapi.NewUpdate(0)
|
||||
u.Timeout = 60
|
||||
|
||||
err = bot.UpdatesChan(u)
|
||||
updates, err := bot.GetUpdatesChan(u)
|
||||
|
||||
for update := range bot.Updates {
|
||||
for update := range updates {
|
||||
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.SendMessage(msg)
|
||||
bot.Send(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleNewWebhook() {
|
||||
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
bot.Debug = true
|
||||
|
||||
log.Printf("Authorized on account %s", bot.Self.UserName)
|
||||
|
||||
_, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,498 @@
|
|||
package tgbotapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Telegram constants
|
||||
const (
|
||||
// APIEndpoint is the endpoint for all API methods, with formatting for Sprintf
|
||||
APIEndpoint = "https://api.telegram.org/bot%s/%s"
|
||||
// FileEndpoint is the endpoint for downloading a file from Telegram
|
||||
FileEndpoint = "https://api.telegram.org/file/bot%s/%s"
|
||||
)
|
||||
|
||||
// Constant values for ChatActions
|
||||
const (
|
||||
ChatTyping = "typing"
|
||||
ChatUploadPhoto = "upload_photo"
|
||||
ChatRecordVideo = "record_video"
|
||||
ChatUploadVideo = "upload_video"
|
||||
ChatRecordAudio = "record_audio"
|
||||
ChatUploadAudio = "upload_audio"
|
||||
ChatUploadDocument = "upload_document"
|
||||
ChatFindLocation = "find_location"
|
||||
)
|
||||
|
||||
// API errors
|
||||
const (
|
||||
// APIForbidden happens when a token is bad
|
||||
APIForbidden = "forbidden"
|
||||
)
|
||||
|
||||
// Constant values for ParseMode in MessageConfig
|
||||
const (
|
||||
ModeMarkdown = "Markdown"
|
||||
)
|
||||
|
||||
//Chattable represents any event in chat(MessageConfig, PhotoConfig, ChatActionConfig and others)
|
||||
type Chattable interface {
|
||||
Values() (url.Values, error)
|
||||
Method() string
|
||||
}
|
||||
|
||||
//Fileable represents any file event(PhotoConfig, DocumentConfig, AudioConfig, VoiceConfig, VideoConfig, StickerConfig)
|
||||
type Fileable interface {
|
||||
Chattable
|
||||
Params() (map[string]string, error)
|
||||
Name() string
|
||||
GetFile() interface{}
|
||||
UseExistingFile() bool
|
||||
}
|
||||
|
||||
// BaseChat is base struct for all chat event(Message, Photo and so on)
|
||||
type BaseChat struct {
|
||||
ChatID int
|
||||
ChannelUsername string
|
||||
ReplyToMessageID int
|
||||
ReplyMarkup interface{}
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of BaseChat
|
||||
func (chat *BaseChat) Values() (url.Values, error) {
|
||||
v := url.Values{}
|
||||
if chat.ChannelUsername != "" {
|
||||
v.Add("chat_id", chat.ChannelUsername)
|
||||
} else {
|
||||
v.Add("chat_id", strconv.Itoa(chat.ChatID))
|
||||
}
|
||||
|
||||
if chat.ReplyToMessageID != 0 {
|
||||
v.Add("reply_to_message_id", strconv.Itoa(chat.ReplyToMessageID))
|
||||
}
|
||||
|
||||
if chat.ReplyMarkup != nil {
|
||||
data, err := json.Marshal(chat.ReplyMarkup)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
|
||||
v.Add("reply_markup", string(data))
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// BaseFile is base struct for all file events(PhotoConfig, DocumentConfig, AudioConfig, VoiceConfig, VideoConfig, StickerConfig)
|
||||
type BaseFile struct {
|
||||
BaseChat
|
||||
FilePath string
|
||||
File interface{}
|
||||
FileID string
|
||||
UseExisting bool
|
||||
}
|
||||
|
||||
// Params returns map[string]string representation of BaseFile
|
||||
func (file BaseFile) Params() (map[string]string, error) {
|
||||
params := make(map[string]string)
|
||||
|
||||
if file.ChannelUsername != "" {
|
||||
params["chat_id"] = file.ChannelUsername
|
||||
} else {
|
||||
params["chat_id"] = strconv.Itoa(file.ChatID)
|
||||
}
|
||||
|
||||
if file.ReplyToMessageID != 0 {
|
||||
params["reply_to_message_id"] = strconv.Itoa(file.ReplyToMessageID)
|
||||
}
|
||||
|
||||
if file.ReplyMarkup != nil {
|
||||
data, err := json.Marshal(file.ReplyMarkup)
|
||||
if err != nil {
|
||||
return params, err
|
||||
}
|
||||
|
||||
params["reply_markup"] = string(data)
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// GetFile returns abstract representation of File inside BaseFile
|
||||
func (file BaseFile) GetFile() interface{} {
|
||||
var result interface{}
|
||||
if file.FilePath == "" {
|
||||
result = file.File
|
||||
} else {
|
||||
result = file.FilePath
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// UseExistingFile returns true if BaseFile contains already uploaded file by FileID
|
||||
func (file BaseFile) UseExistingFile() bool {
|
||||
return file.UseExisting
|
||||
}
|
||||
|
||||
// MessageConfig contains information about a SendMessage request.
|
||||
type MessageConfig struct {
|
||||
BaseChat
|
||||
Text string
|
||||
ParseMode string
|
||||
DisableWebPagePreview bool
|
||||
ReplyMarkup interface{}
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of MessageConfig
|
||||
func (config MessageConfig) Values() (url.Values, error) {
|
||||
v, _ := config.BaseChat.Values()
|
||||
v.Add("text", config.Text)
|
||||
v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview))
|
||||
if config.ParseMode != "" {
|
||||
v.Add("parse_mode", config.ParseMode)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Method returns Telegram API method name for sending Message
|
||||
func (config MessageConfig) Method() string {
|
||||
return "SendMessage"
|
||||
}
|
||||
|
||||
// ForwardConfig contains information about a ForwardMessage request.
|
||||
type ForwardConfig struct {
|
||||
BaseChat
|
||||
FromChatID int
|
||||
FromChannelUsername string
|
||||
MessageID int
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of ForwardConfig
|
||||
func (config ForwardConfig) Values() (url.Values, error) {
|
||||
v, _ := config.BaseChat.Values()
|
||||
v.Add("from_chat_id", strconv.Itoa(config.FromChatID))
|
||||
v.Add("message_id", strconv.Itoa(config.MessageID))
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Method returns Telegram API method name for sending Forward
|
||||
func (config ForwardConfig) Method() string {
|
||||
return "forwardMessage"
|
||||
}
|
||||
|
||||
// PhotoConfig contains information about a SendPhoto request.
|
||||
type PhotoConfig struct {
|
||||
BaseFile
|
||||
Caption string
|
||||
}
|
||||
|
||||
// Params returns map[string]string representation of PhotoConfig
|
||||
func (config PhotoConfig) Params() (map[string]string, error) {
|
||||
params, _ := config.BaseFile.Params()
|
||||
|
||||
if config.Caption != "" {
|
||||
params["caption"] = config.Caption
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of PhotoConfig
|
||||
func (config PhotoConfig) Values() (url.Values, error) {
|
||||
v, _ := config.BaseChat.Values()
|
||||
|
||||
v.Add(config.Name(), config.FileID)
|
||||
if config.Caption != "" {
|
||||
v.Add("caption", config.Caption)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Name return field name for uploading file
|
||||
func (config PhotoConfig) Name() string {
|
||||
return "photo"
|
||||
}
|
||||
|
||||
// Method returns Telegram API method name for sending Photo
|
||||
func (config PhotoConfig) Method() string {
|
||||
return "SendPhoto"
|
||||
}
|
||||
|
||||
// AudioConfig contains information about a SendAudio request.
|
||||
type AudioConfig struct {
|
||||
BaseFile
|
||||
Duration int
|
||||
Performer string
|
||||
Title string
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of AudioConfig
|
||||
func (config AudioConfig) Values() (url.Values, error) {
|
||||
v, _ := config.BaseChat.Values()
|
||||
|
||||
v.Add(config.Name(), config.FileID)
|
||||
if config.Duration != 0 {
|
||||
v.Add("duration", strconv.Itoa(config.Duration))
|
||||
}
|
||||
|
||||
if config.Performer != "" {
|
||||
v.Add("performer", config.Performer)
|
||||
}
|
||||
if config.Title != "" {
|
||||
v.Add("title", config.Title)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Params returns map[string]string representation of AudioConfig
|
||||
func (config AudioConfig) Params() (map[string]string, error) {
|
||||
params, _ := config.BaseFile.Params()
|
||||
|
||||
if config.Duration != 0 {
|
||||
params["duration"] = strconv.Itoa(config.Duration)
|
||||
}
|
||||
|
||||
if config.Performer != "" {
|
||||
params["performer"] = config.Performer
|
||||
}
|
||||
if config.Title != "" {
|
||||
params["title"] = config.Title
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// Name return field name for uploading file
|
||||
func (config AudioConfig) Name() string {
|
||||
return "audio"
|
||||
}
|
||||
|
||||
// Method returns Telegram API method name for sending Audio
|
||||
func (config AudioConfig) Method() string {
|
||||
return "SendAudio"
|
||||
}
|
||||
|
||||
// DocumentConfig contains information about a SendDocument request.
|
||||
type DocumentConfig struct {
|
||||
BaseFile
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of DocumentConfig
|
||||
func (config DocumentConfig) Values() (url.Values, error) {
|
||||
v, _ := config.BaseChat.Values()
|
||||
|
||||
v.Add(config.Name(), config.FileID)
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Params returns map[string]string representation of DocumentConfig
|
||||
func (config DocumentConfig) Params() (map[string]string, error) {
|
||||
params, _ := config.BaseFile.Params()
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// Name return field name for uploading file
|
||||
func (config DocumentConfig) Name() string {
|
||||
return "document"
|
||||
}
|
||||
|
||||
// Method returns Telegram API method name for sending Document
|
||||
func (config DocumentConfig) Method() string {
|
||||
return "sendDocument"
|
||||
}
|
||||
|
||||
// StickerConfig contains information about a SendSticker request.
|
||||
type StickerConfig struct {
|
||||
BaseFile
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of StickerConfig
|
||||
func (config StickerConfig) Values() (url.Values, error) {
|
||||
v, _ := config.BaseChat.Values()
|
||||
|
||||
v.Add(config.Name(), config.FileID)
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Params returns map[string]string representation of StickerConfig
|
||||
func (config StickerConfig) Params() (map[string]string, error) {
|
||||
params, _ := config.BaseFile.Params()
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// Name return field name for uploading file
|
||||
func (config StickerConfig) Name() string {
|
||||
return "sticker"
|
||||
}
|
||||
|
||||
// Method returns Telegram API method name for sending Sticker
|
||||
func (config StickerConfig) Method() string {
|
||||
return "sendSticker"
|
||||
}
|
||||
|
||||
// VideoConfig contains information about a SendVideo request.
|
||||
type VideoConfig struct {
|
||||
BaseFile
|
||||
Duration int
|
||||
Caption string
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of VideoConfig
|
||||
func (config VideoConfig) Values() (url.Values, error) {
|
||||
v, _ := config.BaseChat.Values()
|
||||
|
||||
v.Add(config.Name(), config.FileID)
|
||||
if config.Duration != 0 {
|
||||
v.Add("duration", strconv.Itoa(config.Duration))
|
||||
}
|
||||
if config.Caption != "" {
|
||||
v.Add("caption", config.Caption)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Params returns map[string]string representation of VideoConfig
|
||||
func (config VideoConfig) Params() (map[string]string, error) {
|
||||
params, _ := config.BaseFile.Params()
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// Name return field name for uploading file
|
||||
func (config VideoConfig) Name() string {
|
||||
return "video"
|
||||
}
|
||||
|
||||
// Method returns Telegram API method name for sending Video
|
||||
func (config VideoConfig) Method() string {
|
||||
return "sendVideo"
|
||||
}
|
||||
|
||||
// VoiceConfig contains information about a SendVoice request.
|
||||
type VoiceConfig struct {
|
||||
BaseFile
|
||||
Duration int
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of VoiceConfig
|
||||
func (config VoiceConfig) Values() (url.Values, error) {
|
||||
v, _ := config.BaseChat.Values()
|
||||
|
||||
v.Add(config.Name(), config.FileID)
|
||||
if config.Duration != 0 {
|
||||
v.Add("duration", strconv.Itoa(config.Duration))
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Params returns map[string]string representation of VoiceConfig
|
||||
func (config VoiceConfig) Params() (map[string]string, error) {
|
||||
params, _ := config.BaseFile.Params()
|
||||
|
||||
if config.Duration != 0 {
|
||||
params["duration"] = strconv.Itoa(config.Duration)
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// Name return field name for uploading file
|
||||
func (config VoiceConfig) Name() string {
|
||||
return "voice"
|
||||
}
|
||||
|
||||
// Method returns Telegram API method name for sending Voice
|
||||
func (config VoiceConfig) Method() string {
|
||||
return "sendVoice"
|
||||
}
|
||||
|
||||
// LocationConfig contains information about a SendLocation request.
|
||||
type LocationConfig struct {
|
||||
BaseChat
|
||||
Latitude float64
|
||||
Longitude float64
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of LocationConfig
|
||||
func (config LocationConfig) Values() (url.Values, error) {
|
||||
v, _ := config.BaseChat.Values()
|
||||
|
||||
v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
|
||||
v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64))
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Method returns Telegram API method name for sending Location
|
||||
func (config LocationConfig) Method() string {
|
||||
return "sendLocation"
|
||||
}
|
||||
|
||||
// ChatActionConfig contains information about a SendChatAction request.
|
||||
type ChatActionConfig struct {
|
||||
BaseChat
|
||||
Action string
|
||||
}
|
||||
|
||||
// Values returns url.Values representation of ChatActionConfig
|
||||
func (config ChatActionConfig) Values() (url.Values, error) {
|
||||
v, _ := config.BaseChat.Values()
|
||||
v.Add("action", config.Action)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Method returns Telegram API method name for sending ChatAction
|
||||
func (config ChatActionConfig) Method() string {
|
||||
return "sendChatAction"
|
||||
}
|
||||
|
||||
// UserProfilePhotosConfig contains information about a GetUserProfilePhotos request.
|
||||
type UserProfilePhotosConfig struct {
|
||||
UserID int
|
||||
Offset int
|
||||
Limit int
|
||||
}
|
||||
|
||||
// FileConfig has information about a file hosted on Telegram
|
||||
type FileConfig struct {
|
||||
FileID string
|
||||
}
|
||||
|
||||
// UpdateConfig contains information about a GetUpdates request.
|
||||
type UpdateConfig struct {
|
||||
Offset int
|
||||
Limit int
|
||||
Timeout int
|
||||
}
|
||||
|
||||
// WebhookConfig contains information about a SetWebhook request.
|
||||
type WebhookConfig struct {
|
||||
URL *url.URL
|
||||
Certificate interface{}
|
||||
}
|
||||
|
||||
// FileBytes contains information about a set of bytes to upload as a File.
|
||||
type FileBytes struct {
|
||||
Name string
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
// FileReader contains information about a reader to upload as a File.
|
||||
// If Size is -1, it will read the entire Reader into memory to calculate a Size.
|
||||
type FileReader struct {
|
||||
Name string
|
||||
Reader io.Reader
|
||||
Size int64
|
||||
}
|
61
helpers.go
61
helpers.go
|
@ -10,10 +10,9 @@ import (
|
|||
// chatID is where to send it, text is the message text.
|
||||
func NewMessage(chatID int, text string) MessageConfig {
|
||||
return MessageConfig{
|
||||
ChatID: chatID,
|
||||
BaseChat: BaseChat{ChatID: chatID, ReplyToMessageID: 0},
|
||||
Text: text,
|
||||
DisableWebPagePreview: false,
|
||||
ReplyToMessageID: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +22,7 @@ func NewMessage(chatID int, text string) MessageConfig {
|
|||
// and messageID is the ID of the original message.
|
||||
func NewForward(chatID int, fromChatID int, messageID int) ForwardConfig {
|
||||
return ForwardConfig{
|
||||
ChatID: chatID,
|
||||
BaseChat: BaseChat{ChatID: chatID},
|
||||
FromChatID: fromChatID,
|
||||
MessageID: messageID,
|
||||
}
|
||||
|
@ -36,9 +35,7 @@ func NewForward(chatID int, fromChatID int, messageID int) ForwardConfig {
|
|||
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
|
||||
func NewPhotoUpload(chatID int, file interface{}) PhotoConfig {
|
||||
return PhotoConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingPhoto: false,
|
||||
File: file,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,9 +45,7 @@ func NewPhotoUpload(chatID int, file interface{}) PhotoConfig {
|
|||
// chatID is where to send it, fileID is the ID of the file already uploaded.
|
||||
func NewPhotoShare(chatID int, fileID string) PhotoConfig {
|
||||
return PhotoConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingPhoto: true,
|
||||
FileID: fileID,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,9 +56,7 @@ func NewPhotoShare(chatID int, fileID string) PhotoConfig {
|
|||
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
|
||||
func NewAudioUpload(chatID int, file interface{}) AudioConfig {
|
||||
return AudioConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingAudio: false,
|
||||
File: file,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,9 +66,7 @@ func NewAudioUpload(chatID int, file interface{}) AudioConfig {
|
|||
// chatID is where to send it, fileID is the ID of the audio already uploaded.
|
||||
func NewAudioShare(chatID int, fileID string) AudioConfig {
|
||||
return AudioConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingAudio: true,
|
||||
FileID: fileID,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,9 +77,7 @@ func NewAudioShare(chatID int, fileID string) AudioConfig {
|
|||
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
|
||||
func NewDocumentUpload(chatID int, file interface{}) DocumentConfig {
|
||||
return DocumentConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingDocument: false,
|
||||
File: file,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,9 +87,7 @@ func NewDocumentUpload(chatID int, file interface{}) DocumentConfig {
|
|||
// chatID is where to send it, fileID is the ID of the document already uploaded.
|
||||
func NewDocumentShare(chatID int, fileID string) DocumentConfig {
|
||||
return DocumentConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingDocument: true,
|
||||
FileID: fileID,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,9 +97,7 @@ func NewDocumentShare(chatID int, fileID string) DocumentConfig {
|
|||
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
|
||||
func NewStickerUpload(chatID int, file interface{}) StickerConfig {
|
||||
return StickerConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingSticker: false,
|
||||
File: file,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,9 +107,7 @@ func NewStickerUpload(chatID int, file interface{}) StickerConfig {
|
|||
// chatID is where to send it, fileID is the ID of the sticker already uploaded.
|
||||
func NewStickerShare(chatID int, fileID string) StickerConfig {
|
||||
return StickerConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingSticker: true,
|
||||
FileID: fileID,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,9 +118,7 @@ func NewStickerShare(chatID int, fileID string) StickerConfig {
|
|||
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
|
||||
func NewVideoUpload(chatID int, file interface{}) VideoConfig {
|
||||
return VideoConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingVideo: false,
|
||||
File: file,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,9 +128,7 @@ func NewVideoUpload(chatID int, file interface{}) VideoConfig {
|
|||
// chatID is where to send it, fileID is the ID of the video already uploaded.
|
||||
func NewVideoShare(chatID int, fileID string) VideoConfig {
|
||||
return VideoConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingVideo: true,
|
||||
FileID: fileID,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,9 +139,7 @@ func NewVideoShare(chatID int, fileID string) VideoConfig {
|
|||
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
|
||||
func NewVoiceUpload(chatID int, file interface{}) VoiceConfig {
|
||||
return VoiceConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingVoice: false,
|
||||
File: file,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,9 +149,7 @@ func NewVoiceUpload(chatID int, file interface{}) VoiceConfig {
|
|||
// chatID is where to send it, fileID is the ID of the video already uploaded.
|
||||
func NewVoiceShare(chatID int, fileID string) VoiceConfig {
|
||||
return VoiceConfig{
|
||||
ChatID: chatID,
|
||||
UseExistingVoice: true,
|
||||
FileID: fileID,
|
||||
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,11 +159,9 @@ func NewVoiceShare(chatID int, fileID string) VoiceConfig {
|
|||
// chatID is where to send it, latitude and longitude are coordinates.
|
||||
func NewLocation(chatID int, latitude float64, longitude float64) LocationConfig {
|
||||
return LocationConfig{
|
||||
ChatID: chatID,
|
||||
BaseChat: BaseChat{ChatID: chatID, ReplyToMessageID: 0, ReplyMarkup: nil},
|
||||
Latitude: latitude,
|
||||
Longitude: longitude,
|
||||
ReplyToMessageID: 0,
|
||||
ReplyMarkup: nil,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,7 +171,7 @@ func NewLocation(chatID int, latitude float64, longitude float64) LocationConfig
|
|||
// chatID is where to send it, action should be set via CHAT constants.
|
||||
func NewChatAction(chatID int, action string) ChatActionConfig {
|
||||
return ChatActionConfig{
|
||||
ChatID: chatID,
|
||||
BaseChat: BaseChat{ChatID: chatID},
|
||||
Action: action,
|
||||
}
|
||||
}
|
||||
|
@ -234,7 +207,6 @@ func NewWebhook(link string) WebhookConfig {
|
|||
|
||||
return WebhookConfig{
|
||||
URL: u,
|
||||
Clear: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,7 +219,6 @@ func NewWebhookWithCert(link string, file interface{}) WebhookConfig {
|
|||
|
||||
return WebhookConfig{
|
||||
URL: u,
|
||||
Clear: false,
|
||||
Certificate: file,
|
||||
}
|
||||
}
|
||||
|
|
1125
methods.go
1125
methods.go
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -0,0 +1,18 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIC0zCCAbugAwIBAgIJAPYfllX657axMA0GCSqGSIb3DQEBCwUAMAAwHhcNMTUx
|
||||
MTIxMTExMDQxWhcNMjUwODIwMTExMDQxWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAoMMSIIgYx8pT8Kz1O8Ukd/JVyqBQYRSo0enqEzo7295VROXq
|
||||
TUthbEbdi0OczUfl4IsAWppOSRrDwEguJZ0cJ/r6IxGsbrCdQr2MjgiomYtAXKKQ
|
||||
GAGL5Wls+AzcRNV0OszVJzkDNFYZzgNejyitGJSNEQMyU8r2gyPyIWP9MQKQst8y
|
||||
Mg91R/7l9jwf6AWwNxykZlYZurtsQ6XsBPZpF9YOFL7vZYPhKUFiNEm+74RpojC7
|
||||
Gt6nztYAUI2V/F+1uoXAr8nLpbj9SD0VSwyZLRG1uIVLBzhb0lfOIzAvJ45EKki9
|
||||
nejyoXfH1U5+iMzdSAdcy3MCBhpEZwJPqhDqeQIDAQABo1AwTjAdBgNVHQ4EFgQU
|
||||
JE0RLM+ohLnlDz0Qk0McCxtDK2MwHwYDVR0jBBgwFoAUJE0RLM+ohLnlDz0Qk0Mc
|
||||
CxtDK2MwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEmgME00JYuYZ
|
||||
4wNaGrJskZ05ZnP+TXJusmBui9ToQ4UoykuyY5rsdGQ3SdzXPLdmd2nfMsw63iK2
|
||||
D7rjcH/rmn6fRccZqN0o0SXd/EuHeIoeW1Xnnivbt71b6mcOAeNg1UsMYxnMAVl0
|
||||
ywdkta8gURltagSfXoUbqlnSxn/zCwqaxxcQXA/CnunvRsFtQrwWjDBPg/BPULHX
|
||||
DEh2AactGtnGqEZ5iap/VCOVnmL6iPdJ1x5UIF/gS6U96wL+GHfcs1jCvPg+GEwR
|
||||
3inh9oTXG9L21ge4lbGiPUIMBjtVcB3bXuQbOfec9Cr3ZhcQeZj680BIRxD/pNpA
|
||||
O/XeCfjfkw==
|
||||
-----END CERTIFICATE-----
|
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCgwxIgiBjHylPw
|
||||
rPU7xSR38lXKoFBhFKjR6eoTOjvb3lVE5epNS2FsRt2LQ5zNR+XgiwBamk5JGsPA
|
||||
SC4lnRwn+vojEaxusJ1CvYyOCKiZi0BcopAYAYvlaWz4DNxE1XQ6zNUnOQM0VhnO
|
||||
A16PKK0YlI0RAzJTyvaDI/IhY/0xApCy3zIyD3VH/uX2PB/oBbA3HKRmVhm6u2xD
|
||||
pewE9mkX1g4Uvu9lg+EpQWI0Sb7vhGmiMLsa3qfO1gBQjZX8X7W6hcCvyculuP1I
|
||||
PRVLDJktEbW4hUsHOFvSV84jMC8njkQqSL2d6PKhd8fVTn6IzN1IB1zLcwIGGkRn
|
||||
Ak+qEOp5AgMBAAECggEBAJ/dPCJzlEjhL5XPONLmGXzZ1Gx5/VR86eBMv0O9jhb3
|
||||
wk2QYO3aPxggZGD/rGcKz1L6hzCR77WM0wpb/N/Um1I6pxHGmnU8VjYvLh10CM0f
|
||||
h7JWyfnFV+ubagxFJamhpkJuvKyTaldaI7EU8qxj47Xky18Wka53z6nbTgXcW8Sm
|
||||
V4CJy9OHNgKJQnylX6zOAaxVngSGde3xLslLjsYK4w9b2+OkCSUST2XXdo+ZLXxl
|
||||
cs0lEPFRM1Xh9/E6UrDrJMHHzio53L/W/+a8sIar1upgSY52pyD/tA7VSrAJ9nYC
|
||||
RezOU81VTLfMO+TYmgZzSUQJYh0cR4yqJe+wgl4U550CgYEA1EcS6Z+PO5Pr3u2+
|
||||
XevawSAal6y9ONkkdOoASC977W37nn0E1wlQo41dR6DESCJfiSMeN0KbmXj5Wnc/
|
||||
ADu+73iGwC90G9Qs9sjD7KAFBJvuj0V8hxvpWRdIBBbf7rlOj3CV0iXRYjkJbyJa
|
||||
cxuR0kiv4gTWmm5Cq+5ir8t1Oc8CgYEAwd+xOaDerNR481R+QmENFw+oR2EVMq3Q
|
||||
B/vinLK0PemQWrh32iBcd+vhSilOSQtUm1nko1jLK8C4s8X2vZYua4m5tcK9VqCt
|
||||
maCCq/ffxzsoW/GN8japnduz+qA+hKWJzW/aYR8tsOeqzjVqj4iIqPI4HuokrDi/
|
||||
UD/QLgq5UTcCgYEAk2ZC0Kx15dXB7AtDq63xOTcUoAtXXRkSgohV58npEKXVGWkQ
|
||||
Kk0SjG7Fvc35XWlY0z3qZk6/AuOIqfOxcHUMEPatAtgwlH5RNo+T1EQNF/U6wotq
|
||||
e9q6vp026XgEyJwt29Y+giy2ZrDaRywgiFs1d0H3t0bKyXMUopQmPJFXdesCgYEA
|
||||
psCxXcDpZjxGX/zPsGZrbOdxtRtisTlg0k0rp93pO8tV90HtDHeDMT54g2ItzJPr
|
||||
TMev6XOpJNPZyf6+8GhpOuO2EQkT85u2VYoCeslz95gBabvFfIzZrUZYcnw76bm8
|
||||
YjAP5DN+CEfq2PyG0Df+W1ojPSvlKSCSJQMOG1vr81cCgYEAkjPY5WR99uJxYBNI
|
||||
OTFMSkETgDUbPXBu/E/h5Dtn79v8Moj9FvC7+q6sg9qXhrGhfK2xDev3/sTrbS/E
|
||||
Gcf8UNIne3AXsoAS8MtkOwJXHkYaTIboIYgDX4LlDmbGQlIRaWgyh2POI6VtjLBT
|
||||
ms6AdsdpIB6As9xNUBUwj/RnTZQ=
|
||||
-----END PRIVATE KEY-----
|
Binary file not shown.
Binary file not shown.
|
@ -1,92 +0,0 @@
|
|||
// Package tgutils provides extra functions to make certain tasks easier.
|
||||
package tgutils
|
||||
|
||||
import (
|
||||
"github.com/syfaro/telegram-bot-api"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var rand uint32
|
||||
var randmu sync.Mutex
|
||||
|
||||
func reseed() uint32 {
|
||||
return uint32(time.Now().UnixNano() + int64(os.Getpid()))
|
||||
}
|
||||
|
||||
func nextSuffix() string {
|
||||
randmu.Lock()
|
||||
r := rand
|
||||
if r == 0 {
|
||||
r = reseed()
|
||||
}
|
||||
r = r*1664525 + 1013904223 // constants from Numerical Recipes
|
||||
rand = r
|
||||
randmu.Unlock()
|
||||
return strconv.Itoa(int(1e9 + r%1e9))[1:]
|
||||
}
|
||||
|
||||
// this function ripped from ioutils.TempFile, except with a suffix, instead of prefix.
|
||||
func tempFileWithSuffix(dir, suffix string) (f *os.File, err error) {
|
||||
if dir == "" {
|
||||
dir = os.TempDir()
|
||||
}
|
||||
|
||||
nconflict := 0
|
||||
for i := 0; i < 10000; i++ {
|
||||
name := filepath.Join(dir, nextSuffix()+suffix)
|
||||
f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||||
if os.IsExist(err) {
|
||||
if nconflict++; nconflict > 10 {
|
||||
randmu.Lock()
|
||||
rand = reseed()
|
||||
randmu.Unlock()
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeAudio takes a file and attempts to convert it to a .ogg for Telegram.
|
||||
// It then updates the path to the audio file in the AudioConfig.
|
||||
//
|
||||
// This function requires ffmpeg and opusenc to be installed on the system!
|
||||
func EncodeAudio(audio *tgbotapi.AudioConfig) error {
|
||||
f, err := tempFileWithSuffix(os.TempDir(), "_tgutils.ogg")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
ffmpegArgs := []string{
|
||||
"-i",
|
||||
audio.FilePath,
|
||||
"-f",
|
||||
"wav",
|
||||
"-",
|
||||
}
|
||||
|
||||
opusArgs := []string{
|
||||
"--bitrate",
|
||||
"256",
|
||||
"-",
|
||||
f.Name(),
|
||||
}
|
||||
|
||||
c1 := exec.Command("ffmpeg", ffmpegArgs...)
|
||||
c2 := exec.Command("opusenc", opusArgs...)
|
||||
|
||||
c2.Stdin, _ = c1.StdoutPipe()
|
||||
c2.Stdout = os.Stdout
|
||||
c2.Start()
|
||||
c1.Run()
|
||||
c2.Wait()
|
||||
|
||||
return nil
|
||||
}
|
11
types.go
11
types.go
|
@ -3,6 +3,7 @@ package tgbotapi
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -112,6 +113,16 @@ func (m *Message) IsGroup() bool {
|
|||
return m.From.ID != m.Chat.ID
|
||||
}
|
||||
|
||||
// IsCommand returns true if message starts from /
|
||||
func (m *Message) IsCommand() bool {
|
||||
return m.Text != "" && m.Text[0] == '/'
|
||||
}
|
||||
|
||||
// Command returns first word from message
|
||||
func (m *Message) Command() string {
|
||||
return strings.Split(m.Text, " ")[0]
|
||||
}
|
||||
|
||||
// PhotoSize contains information about photos, including ID and Width and Height.
|
||||
type PhotoSize struct {
|
||||
FileID string `json:"file_id"`
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
package tgbotapi_test
|
||||
|
||||
import (
|
||||
"github.com/Syfaro/telegram-bot-api"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestUserStringWith(t *testing.T) {
|
||||
user := tgbotapi.User{0, "Test", "Test", ""}
|
||||
|
||||
if user.String() != "Test Test" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserStringWithUserName(t *testing.T) {
|
||||
user := tgbotapi.User{0, "Test", "Test", "@test"}
|
||||
|
||||
if user.String() != "@test" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageIsGroup(t *testing.T) {
|
||||
from := tgbotapi.User{ID: 0}
|
||||
chat := tgbotapi.Chat{ID: 10}
|
||||
message := tgbotapi.Message{From: from, Chat: chat}
|
||||
|
||||
if message.IsGroup() != true {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageTime(t *testing.T) {
|
||||
message := tgbotapi.Message{Date: 0}
|
||||
|
||||
date := time.Unix(0, 0)
|
||||
if message.Time() != date {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatIsPrivate(t *testing.T) {
|
||||
chat := tgbotapi.Chat{ID: 10, Type: "private"}
|
||||
|
||||
if chat.IsPrivate() != true {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatIsGroup(t *testing.T) {
|
||||
chat := tgbotapi.Chat{ID: 10, Type: "group"}
|
||||
|
||||
if chat.IsGroup() != true {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatIsChannel(t *testing.T) {
|
||||
chat := tgbotapi.Chat{ID: 10, Type: "channel"}
|
||||
|
||||
if chat.IsChannel() != true {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileLink(t *testing.T) {
|
||||
file := tgbotapi.File{FilePath: "test/test.txt"}
|
||||
|
||||
if file.Link("token") != "https://api.telegram.org/file/bottoken/test/test.txt" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
33
updates.go
33
updates.go
|
@ -1,33 +0,0 @@
|
|||
package tgbotapi
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UpdatesChan starts a channel for getting updates.
|
||||
func (bot *BotAPI) UpdatesChan(config UpdateConfig) error {
|
||||
bot.Updates = make(chan Update, 100)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
updates, err := bot.GetUpdates(config)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
log.Println("Failed to get updates, retrying in 3 seconds...")
|
||||
time.Sleep(time.Second * 3)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
for _, update := range updates {
|
||||
if update.UpdateID >= config.Offset {
|
||||
config.Offset = update.UpdateID + 1
|
||||
bot.Updates <- update
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
21
webhook.go
21
webhook.go
|
@ -1,21 +0,0 @@
|
|||
package tgbotapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ListenForWebhook registers a http handler for a webhook.
|
||||
func (bot *BotAPI) ListenForWebhook(pattern string) {
|
||||
bot.Updates = make(chan Update, 100)
|
||||
|
||||
http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
bytes, _ := ioutil.ReadAll(r.Body)
|
||||
|
||||
var update Update
|
||||
json.Unmarshal(bytes, &update)
|
||||
|
||||
bot.Updates <- update
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue