Albums added, this commit resolves #103.

pull/108/head
Ian Byrd 7 years ago
parent 33e1846abd
commit 83eb527fe5
No known key found for this signature in database
GPG Key ID: 598F598CA3B8055F

@ -5,7 +5,7 @@
[![Travis](https://travis-ci.org/tucnak/telebot.svg?branch=v2)](https://travis-ci.org/tucnak/telebot)
```bash
go get gopkg.in/tucnak/telebot.v2
go get -u gopkg.in/tucnak/telebot.v2
```
Telebot is a bot framework for [Telegram](https://telegram.org) [Bot API](https://core.telegram.org/bots/api).

@ -39,52 +39,59 @@ func (b *Bot) sendCommand(method string, payload interface{}) ([]byte, error) {
return json, nil
}
func (b *Bot) sendFile(method, name, path string, params map[string]string) ([]byte, error) {
file, err := os.Open(path)
if err != nil {
return []byte{}, wrapSystem(err)
}
defer file.Close()
func (b *Bot) sendFiles(
method string,
files map[string]string,
params map[string]string) ([]byte, error) {
// ---
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile(name, filepath.Base(path))
if err != nil {
return []byte{}, wrapSystem(err)
}
if _, err = io.Copy(part, file); err != nil {
return []byte{}, wrapSystem(err)
for name, path := range files {
file, err := os.Open(path)
if err != nil {
return nil, wrapSystem(err)
}
defer file.Close()
part, err := writer.CreateFormFile(name, filepath.Base(path))
if err != nil {
return nil, wrapSystem(err)
}
if _, err = io.Copy(part, file); err != nil {
return nil, wrapSystem(err)
}
}
for field, value := range params {
writer.WriteField(field, value)
}
if err = writer.Close(); err != nil {
return []byte{}, wrapSystem(err)
if err := writer.Close(); err != nil {
return nil, wrapSystem(err)
}
url := fmt.Sprintf("https://api.telegram.org/bot%s/%s", b.Token, method)
req, err := http.NewRequest("POST", url, body)
if err != nil {
return []byte{}, wrapSystem(err)
return nil, wrapSystem(err)
}
req.Header.Add("Content-Type", writer.FormDataContentType())
resp, err := http.DefaultClient.Do(req)
if err != nil {
return []byte{}, errors.Wrap(err, "http.Post failed")
return nil, errors.Wrap(err, "http.Post failed")
}
if resp.StatusCode == http.StatusInternalServerError {
return []byte{}, errors.New("api error: internal server error")
return nil, errors.New("api error: internal server error")
}
json, err := ioutil.ReadAll(resp.Body)
if err != nil {
return []byte{}, wrapSystem(err)
return nil, wrapSystem(err)
}
return json, nil
@ -103,11 +110,12 @@ func (b *Bot) sendObject(f *File, what string, params map[string]string) (*Messa
if f.InCloud() {
params[what] = f.FileID
respJSON, err = b.sendCommand(sendWhat, params)
} else if f.FileURL != nil {
params[what] = f.FileURL.String()
} else if f.FileURL != "" {
params[what] = f.FileURL
respJSON, err = b.sendCommand(sendWhat, params)
} else {
respJSON, err = b.sendFile(sendWhat, what, f.FileLocal, params)
respJSON, err = b.sendFiles(sendWhat,
map[string]string{what: f.FileLocal}, params)
}
if err != nil {

@ -276,8 +276,97 @@ func (b *Bot) Send(to Recipient, what interface{}, options ...interface{}) (*Mes
}
}
// Reply behaves just like Send() with an exception of "reply-to" indicator.
// SendAlbum is used when sending multiple instances of media as a single
// message (so-called album).
//
// From all existing options, it only supports telebot.Silent.
func (b *Bot) SendAlbum(to Recipient, a Album, options ...interface{}) ([]Message, error) {
media := make([]string, len(a))
files := make(map[string]string)
for i, x := range a {
var (
f *File
caption string
mediaRepr, mediaType string
)
switch y := x.(type) {
case *Photo:
f = &y.File
mediaType = "photo"
caption = y.Caption
case *Video:
f = &y.File
mediaType = "video"
caption = y.Caption
default:
return nil, errors.Errorf("telebot: album entry #%d is not valid", i)
}
if f.InCloud() {
mediaRepr = f.FileID
} else if f.FileURL != "" {
mediaRepr = f.FileURL
} else if f.OnDisk() {
mediaRepr = fmt.Sprintf("attach://%d", i)
files[strconv.Itoa(i)] = f.FileLocal
} else {
return nil, errors.Errorf(
"telebot: album entry #%d doesn't exist anywhere", i)
}
jsonRepr, _ := json.Marshal(map[string]string{
"type": mediaType,
"media": mediaRepr,
"caption": caption,
})
media[i] = string(jsonRepr)
}
params := map[string]string{
"chat_id": to.Recipient(),
"media": "[" + strings.Join(media, ",") + "]",
}
sendOpts := extractOptions(options)
embedSendOptions(params, sendOpts)
respJSON, err := b.sendFiles("sendMediaGroup", files, params)
var resp struct {
Ok bool
Result []Message
Description string
}
err = json.Unmarshal(respJSON, &resp)
if err != nil {
return nil, errors.Wrap(err, "bad response json")
}
if !resp.Ok {
return nil, errors.Errorf("api error: %s", resp.Description)
}
for attachName, _ := range files {
i, _ := strconv.Atoi(attachName)
var newID string
if resp.Result[i].Photo != nil {
newID = resp.Result[i].Photo.FileID
} else {
newID = resp.Result[i].Video.FileID
}
a[i].MediaFile().FileID = newID
}
return resp.Result, nil
}
// Reply behaves just like Send() with an exception of "reply-to" indicator.
func (b *Bot) Reply(to *Message, what interface{}, options ...interface{}) (*Message, error) {
// This function will panic upon unsupported payloads and options!
sendOpts := extractOptions(options)
@ -302,9 +391,6 @@ func (b *Bot) Forward(to Recipient, what *Message, options ...interface{}) (*Mes
}
sendOpts := extractOptions(options)
if sendOpts == nil {
sendOpts = &SendOptions{}
}
embedSendOptions(params, sendOpts)
respJSON, err := b.sendCommand("forwardMessage", params)

@ -1,7 +1,6 @@
package telebot
import (
"net/url"
"os"
)
@ -14,10 +13,10 @@ type File struct {
FilePath string `json:"file_path"`
// file on local file system.
FileLocal string
FileLocal string `json:"file_local"`
// file on the internet
FileURL *url.URL
FileURL string `json:"file_url"`
}
// FromDisk constructs a new local (on-disk) file object.
@ -42,8 +41,8 @@ func FromDisk(filename string) File {
//
// photo := &tb.Photo{File: tb.FromURL("https://site.com/picture.jpg")}
//
func FromURL(u *url.URL) File {
return File{FileURL: u}
func FromURL(url string) File {
return File{FileURL: url}
}
func (f *File) stealRef(g *File) {
@ -51,7 +50,7 @@ func (f *File) stealRef(g *File) {
f.FileLocal = g.FileLocal
}
if g.FileURL != nil {
if g.FileURL != "" {
f.FileURL = g.FileURL
}
}

@ -1,15 +1,21 @@
package telebot
import "encoding/json"
import (
"encoding/json"
)
type photoSize struct {
File
Width int `json:"width"`
Height int `json:"height"`
// (Optional)
Caption string `json:"caption,omitempty"`
// Album lets you group multiple media (so-called InputMedia)
// into a single messsage.
//
// On older clients albums look like N regular messages.
type Album []InputMedia
// InputMedia is a generic type for all kinds of media you
// can put into an album.
type InputMedia interface {
// As some files must be uploaded (instead of referencing)
// outer layers of Telebot require it.
MediaFile() *File
}
// Photo object represents a single photo file.
@ -23,6 +29,18 @@ type Photo struct {
Caption string `json:"caption,omitempty"`
}
type photoSize struct {
File
Width int `json:"width"`
Height int `json:"height"`
Caption string `json:"caption,omitempty"`
}
// MediaFile returns &Photo.File
func (p *Photo) MediaFile() *File {
return &p.File
}
// UnmarshalJSON is custom unmarshaller required to abstract
// away the hassle of treating different thumbnail sizes.
// Instead, Telebot chooses the hi-res one and just sticks to
@ -58,7 +76,7 @@ type Audio struct {
File
// Duration of the recording in seconds as defined by sender.
Duration int `json:"duration"`
Duration int `json:"duration,omitempty"`
// (Optional)
Caption string `json:"caption,omitempty"`
@ -83,18 +101,24 @@ type Document struct {
// Video object represents a video file.
type Video struct {
Audio
File
Width int `json:"width"`
Height int `json:"height"`
Duration int `json:"duration"`
Duration int `json:"duration,omitempty"`
// (Optional)
Caption string `json:"caption,omitempty"`
Thumbnail *Photo `json:"thumb,omitempty"`
MIME string `json:"mime_type,omitempty"`
}
// MediaFile returns &Video.File
func (v *Video) MediaFile() *File {
return &v.File
}
// Voice object represents a voice note.
type Voice struct {
File

@ -108,7 +108,7 @@ type Message struct {
NewChatTitle string `json:"new_chat_title"`
// For a service message, represents all available
// thumbnails of new chat photo.
// thumbnails of the new chat photo.
//
// Sender would lead to a User, capable of change.
NewChatPhoto []Photo `json:"new_chat_photo"`

Loading…
Cancel
Save