From 91e1825b161873895b340ec24ff64c8ceb66898c Mon Sep 17 00:00:00 2001 From: Emad Ghasemi Date: Wed, 5 Aug 2015 21:30:54 +0430 Subject: [PATCH 1/7] Update types.go --- types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types.go b/types.go index fda3298..e9bcd32 100644 --- a/types.go +++ b/types.go @@ -79,7 +79,7 @@ type Message struct { NewChatParticipant User `json:"new_chat_participant"` LeftChatParticipant User `json:"left_chat_participant"` NewChatTitle string `json:"new_chat_title"` - NewChatPhoto string `json:"new_chat_photo"` + NewChatPhoto []PhotoSize `json:"new_chat_photo"` DeleteChatPhoto bool `json:"delete_chat_photo"` GroupChatCreated bool `json:"group_chat_created"` } From ca93bb337c3d071d8f324c5933390f68341c1dc3 Mon Sep 17 00:00:00 2001 From: Emad Ghasemi Date: Wed, 5 Aug 2015 21:31:41 +0430 Subject: [PATCH 2/7] Fixed Message.NewChatPhoto --- types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types.go b/types.go index e9bcd32..82e6814 100644 --- a/types.go +++ b/types.go @@ -79,7 +79,7 @@ type Message struct { NewChatParticipant User `json:"new_chat_participant"` LeftChatParticipant User `json:"left_chat_participant"` NewChatTitle string `json:"new_chat_title"` - NewChatPhoto []PhotoSize `json:"new_chat_photo"` + NewChatPhoto []PhotoSize `json:"new_chat_photo"` DeleteChatPhoto bool `json:"delete_chat_photo"` GroupChatCreated bool `json:"group_chat_created"` } From d06eead68daccd87bfdd6a10d289fd47cdeec987 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Sun, 9 Aug 2015 21:12:15 -0500 Subject: [PATCH 3/7] don't panic for failed updates, even in debug (fixes #14) --- updates.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/updates.go b/updates.go index a3c6c46..f790a88 100644 --- a/updates.go +++ b/updates.go @@ -13,13 +13,9 @@ func (bot *BotAPI) UpdatesChan(config UpdateConfig) error { for { updates, err := bot.GetUpdates(config) if err != nil { - if bot.Debug { - panic(err) - } else { - log.Println(err) - log.Println("Failed to get updates, retrying in 3 seconds...") - time.Sleep(time.Second * 3) - } + log.Println(err) + log.Println("Failed to get updates, retrying in 3 seconds...") + time.Sleep(time.Second * 3) continue } From 5a72ea681439dd22b56ea4c29bd8c58ef52dd751 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 10 Aug 2015 07:41:44 -0500 Subject: [PATCH 4/7] switch to streaming multipart, reduces memory usage for uploads --- methods.go | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/methods.go b/methods.go index 7b25dc3..c345a96 100644 --- a/methods.go +++ b/methods.go @@ -1,14 +1,12 @@ package tgbotapi import ( - "bytes" "encoding/json" "errors" "fmt" - "io" + "github.com/technoweenie/multipartstreamer" "io/ioutil" "log" - "mime/multipart" "net/http" "net/url" "os" @@ -180,46 +178,32 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, // // Requires the parameter to hold the file not be in the params. func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, filename string) (APIResponse, error) { - var b bytes.Buffer - w := multipart.NewWriter(&b) - f, err := os.Open(filename) if err != nil { return APIResponse{}, err } + defer f.Close() - fw, err := w.CreateFormFile(fieldname, filename) + fi, err := os.Stat(filename) if err != nil { return APIResponse{}, err } - if _, err = io.Copy(fw, f); err != nil { - return APIResponse{}, err - } + ms := multipartstreamer.New() + ms.WriteFields(params) + ms.WriteReader(fieldname, f.Name(), fi.Size(), f) - for key, val := range params { - if fw, err = w.CreateFormField(key); err != nil { - return APIResponse{}, err - } - - if _, err = fw.Write([]byte(val)); err != nil { - return APIResponse{}, err - } - } - - w.Close() - - req, err := http.NewRequest("POST", fmt.Sprintf(APIEndpoint, bot.Token, endpoint), &b) + req, err := http.NewRequest("POST", fmt.Sprintf(APIEndpoint, bot.Token, endpoint), nil) + ms.SetupRequest(req) if err != nil { return APIResponse{}, err } - req.Header.Set("Content-Type", w.FormDataContentType()) - 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 { From 3a401563b5a6428f3d6a4f76d6d2d8e109ff52c1 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 10 Aug 2015 15:55:46 -0500 Subject: [PATCH 5/7] utils package, currently only for converting audio files --- tgutils/audio.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tgutils/audio.go diff --git a/tgutils/audio.go b/tgutils/audio.go new file mode 100644 index 0000000..b9a272f --- /dev/null +++ b/tgutils/audio.go @@ -0,0 +1,95 @@ +// Package tgutils provides extra functions to make certain tasks easier. +package tgutils + +import ( + "github.com/syfaro/telegram-bot-api" + "log" + "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 returns an 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() + + log.Println(f.Name()) + + 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 +} From b92bbece9f8dad977b986a5c9ac2b5302ab6ca00 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 10 Aug 2015 15:58:15 -0500 Subject: [PATCH 6/7] fix docs in encodeaudio, remove log --- tgutils/audio.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tgutils/audio.go b/tgutils/audio.go index b9a272f..f7a0034 100644 --- a/tgutils/audio.go +++ b/tgutils/audio.go @@ -3,7 +3,6 @@ package tgutils import ( "github.com/syfaro/telegram-bot-api" - "log" "os" "os/exec" "path/filepath" @@ -55,7 +54,7 @@ func tempFileWithSuffix(dir, suffix string) (f *os.File, err error) { } // EncodeAudio takes a file and attempts to convert it to a .ogg for Telegram. -// It then returns an AudioConfig. +// 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 { @@ -65,8 +64,6 @@ func EncodeAudio(audio *tgbotapi.AudioConfig) error { } defer f.Close() - log.Println(f.Name()) - ffmpegArgs := []string{ "-i", audio.FilePath, From d54197d756fb2036833c1d50c63a9be81a66c79b Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 10 Aug 2015 16:02:29 -0500 Subject: [PATCH 7/7] mention encodeaudio function on sendaudio function --- methods.go | 1 + 1 file changed, 1 insertion(+) diff --git a/methods.go b/methods.go index c345a96..52bfb39 100644 --- a/methods.go +++ b/methods.go @@ -375,6 +375,7 @@ func (bot *BotAPI) SendPhoto(config PhotoConfig) (Message, error) { // SendAudio sends or uploads an audio clip to a chat. // If using a file, the file must be encoded as an .ogg with OPUS. +// You may use the tgutils.EncodeAudio func to assist you with this, if needed. // // Requires ChatID and FileID OR FilePath. // ReplyToMessageID and ReplyMarkup are optional.