Fully refactored high level API

This commit is contained in:
Alexander Kiryukhin 2019-09-05 22:43:32 +03:00
parent 29322bf303
commit 6e4ade909d
14 changed files with 354 additions and 351 deletions

View file

@ -19,66 +19,9 @@
## Пример
```go
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"github.com/neonxp/tamtam"
)
func main() {
api := tamtam.New(os.Getenv("TOKEN"))
info, err := api.Bots.GetBot() // Простой метод
log.Printf("Get me: %#v %#v", info, err)
ctx, cancel := context.WithCancel(context.Background())
go func() {
exit := make(chan os.Signal)
signal.Notify(exit, os.Kill, os.Interrupt)
<-exit
cancel()
}()
for upd := range api.GetUpdates(ctx) { // Чтение из канала с обновлениями
log.Printf("Received: %#v", upd)
switch upd := upd.(type) { // Определение типа пришедшего обновления
case *tamtam.MessageCreatedUpdate:
// Создание клавиатуры
keyboard := api.Messages.NewKeyboardBuilder()
keyboard.
AddRow().
AddGeolocation("Прислать геолокацию", true).
AddContact("Прислать контакт")
keyboard.
AddRow().
AddLink("Библиотека", tamtam.POSITIVE, "https://github.com/neonxp/tamtam").
AddCallback("Колбек 1", tamtam.NEGATIVE, "callback_1").
AddCallback("Колбек 2", tamtam.NEGATIVE, "callback_2")
// Отправка сообщения с клавиатурой
res, err := api.Messages.SendMessage(0, upd.Message.Sender.UserId, &tamtam.NewMessageBody{
Text: fmt.Sprintf("Hello, %s! Your message: %s", upd.Message.Sender.Name, upd.Message.Body.Text),
Attachments: []interface{}{
tamtam.NewInlineKeyboardAttachmentRequest(keyboard.Build()),
},
})
log.Printf("Answer: %#v %#v", res, err)
case *tamtam.MessageCallbackUpdate:
res, err := api.Messages.SendMessage(0, upd.Callback.User.UserId, &tamtam.NewMessageBody{
Text: "Callback: " + upd.Callback.Payload,
})
log.Printf("Answer: %#v %#v", res, err)
default:
log.Printf("Unknown type: %#v", upd)
}
}
}
```
* [Пример с отправкой фото](https://github.com/neonxp/tamtam/blob/master/examples/example.go)
* [Пример с longpolling](https://github.com/neonxp/tamtam/blob/master/examples/example_longpolling.go)
* [Пример с webhook](https://github.com/neonxp/tamtam/blob/master/examples/example_webhook.go)
## Автор

94
api.go
View file

@ -11,6 +11,8 @@ import (
"net/url"
"strconv"
"time"
"github.com/neonxp/tamtam/schemes"
)
//Api implements main part of TamTam API
@ -42,106 +44,106 @@ func New(key string) *Api {
}
}
func (a *Api) bytesToProperUpdate(b []byte) UpdateInterface {
u := new(Update)
func (a *Api) bytesToProperUpdate(b []byte) schemes.UpdateInterface {
u := new(schemes.Update)
_ = json.Unmarshal(b, u)
switch u.GetUpdateType() {
case TypeMessageCallback:
upd := new(MessageCallbackUpdate)
case schemes.TypeMessageCallback:
upd := new(schemes.MessageCallbackUpdate)
_ = json.Unmarshal(b, upd)
return upd
case TypeMessageCreated:
upd := new(MessageCreatedUpdate)
case schemes.TypeMessageCreated:
upd := new(schemes.MessageCreatedUpdate)
_ = json.Unmarshal(b, upd)
for _, att := range upd.Message.Body.RawAttachments {
upd.Message.Body.Attachments = append(upd.Message.Body.Attachments, a.bytesToProperAttachment(att))
}
return upd
case TypeMessageRemoved:
upd := new(MessageRemovedUpdate)
case schemes.TypeMessageRemoved:
upd := new(schemes.MessageRemovedUpdate)
_ = json.Unmarshal(b, upd)
return upd
case TypeMessageEdited:
upd := new(MessageEditedUpdate)
case schemes.TypeMessageEdited:
upd := new(schemes.MessageEditedUpdate)
_ = json.Unmarshal(b, upd)
for _, att := range upd.Message.Body.RawAttachments {
upd.Message.Body.Attachments = append(upd.Message.Body.Attachments, a.bytesToProperAttachment(att))
}
return upd
case TypeBotAdded:
upd := new(BotAddedToChatUpdate)
case schemes.TypeBotAdded:
upd := new(schemes.BotAddedToChatUpdate)
_ = json.Unmarshal(b, upd)
return upd
case TypeBotRemoved:
upd := new(BotRemovedFromChatUpdate)
case schemes.TypeBotRemoved:
upd := new(schemes.BotRemovedFromChatUpdate)
_ = json.Unmarshal(b, upd)
return upd
case TypeUserAdded:
upd := new(UserAddedToChatUpdate)
case schemes.TypeUserAdded:
upd := new(schemes.UserAddedToChatUpdate)
_ = json.Unmarshal(b, upd)
return upd
case TypeUserRemoved:
upd := new(UserRemovedFromChatUpdate)
case schemes.TypeUserRemoved:
upd := new(schemes.UserRemovedFromChatUpdate)
_ = json.Unmarshal(b, upd)
return upd
case TypeBotStarted:
upd := new(BotStartedUpdate)
case schemes.TypeBotStarted:
upd := new(schemes.BotStartedUpdate)
_ = json.Unmarshal(b, upd)
return upd
case TypeChatTitleChanged:
upd := new(ChatTitleChangedUpdate)
case schemes.TypeChatTitleChanged:
upd := new(schemes.ChatTitleChangedUpdate)
_ = json.Unmarshal(b, upd)
return upd
}
return nil
}
func (a *Api) bytesToProperAttachment(b []byte) AttachmentInterface {
attachment := new(Attachment)
func (a *Api) bytesToProperAttachment(b []byte) schemes.AttachmentInterface {
attachment := new(schemes.Attachment)
_ = json.Unmarshal(b, attachment)
switch attachment.GetAttachmentType() {
case AttachmentAudio:
res := new(AudioAttachment)
case schemes.AttachmentAudio:
res := new(schemes.AudioAttachment)
_ = json.Unmarshal(b, res)
return res
case AttachmentContact:
res := new(ContactAttachment)
case schemes.AttachmentContact:
res := new(schemes.ContactAttachment)
_ = json.Unmarshal(b, res)
return res
case AttachmentFile:
res := new(FileAttachment)
case schemes.AttachmentFile:
res := new(schemes.FileAttachment)
_ = json.Unmarshal(b, res)
return res
case AttachmentImage:
res := new(PhotoAttachment)
case schemes.AttachmentImage:
res := new(schemes.PhotoAttachment)
_ = json.Unmarshal(b, res)
return res
case AttachmentKeyboard:
res := new(InlineKeyboardAttachment)
case schemes.AttachmentKeyboard:
res := new(schemes.InlineKeyboardAttachment)
_ = json.Unmarshal(b, res)
return res
case AttachmentLocation:
res := new(LocationAttachment)
case schemes.AttachmentLocation:
res := new(schemes.LocationAttachment)
_ = json.Unmarshal(b, res)
return res
case AttachmentShare:
res := new(ShareAttachment)
case schemes.AttachmentShare:
res := new(schemes.ShareAttachment)
_ = json.Unmarshal(b, res)
return res
case AttachmentSticker:
res := new(StickerAttachment)
case schemes.AttachmentSticker:
res := new(schemes.StickerAttachment)
_ = json.Unmarshal(b, res)
return res
case AttachmentVideo:
res := new(VideoAttachment)
case schemes.AttachmentVideo:
res := new(schemes.VideoAttachment)
_ = json.Unmarshal(b, res)
return res
}
return attachment
}
func (a *Api) getUpdates(limit int, timeout int, marker int, types []string) (*UpdateList, error) {
result := new(UpdateList)
func (a *Api) getUpdates(limit int, timeout int, marker int, types []string) (*schemes.UpdateList, error) {
result := new(schemes.UpdateList)
values := url.Values{}
if limit > 0 {
values.Set("limit", strconv.Itoa(limit))
@ -171,8 +173,8 @@ func (a *Api) getUpdates(limit int, timeout int, marker int, types []string) (*U
}
//GetUpdates returns updates channel
func (a *Api) GetUpdates(ctx context.Context) chan UpdateInterface {
ch := make(chan UpdateInterface)
func (a *Api) GetUpdates(ctx context.Context) chan schemes.UpdateInterface {
ch := make(chan schemes.UpdateInterface)
go func() {
for {
select {

10
bots.go
View file

@ -5,6 +5,8 @@ import (
"log"
"net/http"
"net/url"
"github.com/neonxp/tamtam/schemes"
)
type bots struct {
@ -16,8 +18,8 @@ func newBots(client *client) *bots {
}
//GetBot returns info about current bot. Current bot can be identified by access token. Method returns bot identifier, name and avatar (if any)
func (a *bots) GetBot() (*BotInfo, error) {
result := new(BotInfo)
func (a *bots) GetBot() (*schemes.BotInfo, error) {
result := new(schemes.BotInfo)
values := url.Values{}
body, err := a.client.request(http.MethodGet, "me", values, nil)
if err != nil {
@ -32,8 +34,8 @@ func (a *bots) GetBot() (*BotInfo, error) {
}
//PatchBot edits current bot info. Fill only the fields you want to update. All remaining fields will stay untouched
func (a *bots) PatchBot(patch *BotPatch) (*BotInfo, error) {
result := new(BotInfo)
func (a *bots) PatchBot(patch *schemes.BotPatch) (*schemes.BotInfo, error) {
result := new(schemes.BotInfo)
values := url.Values{}
body, err := a.client.request(http.MethodPatch, "me", values, patch)
if err != nil {

View file

@ -7,6 +7,8 @@ import (
"net/http"
"net/url"
"strconv"
"github.com/neonxp/tamtam/schemes"
)
type chats struct {
@ -18,8 +20,8 @@ func newChats(client *client) *chats {
}
//GetChats returns information about chats that bot participated in: a result list and marker points to the next page
func (a *chats) GetChats(count, marker int) (*ChatList, error) {
result := new(ChatList)
func (a *chats) GetChats(count, marker int) (*schemes.ChatList, error) {
result := new(schemes.ChatList)
values := url.Values{}
if count > 0 {
values.Set("count", strconv.Itoa(int(count)))
@ -40,8 +42,8 @@ func (a *chats) GetChats(count, marker int) (*ChatList, error) {
}
//GetChat returns info about chat
func (a *chats) GetChat(chatID int) (*Chat, error) {
result := new(Chat)
func (a *chats) GetChat(chatID int) (*schemes.Chat, error) {
result := new(schemes.Chat)
values := url.Values{}
body, err := a.client.request(http.MethodGet, fmt.Sprintf("chats/%d", chatID), values, nil)
if err != nil {
@ -56,8 +58,8 @@ func (a *chats) GetChat(chatID int) (*Chat, error) {
}
//GetChatMembership returns chat membership info for current bot
func (a *chats) GetChatMembership(chatID int) (*ChatMember, error) {
result := new(ChatMember)
func (a *chats) GetChatMembership(chatID int) (*schemes.ChatMember, error) {
result := new(schemes.ChatMember)
values := url.Values{}
body, err := a.client.request(http.MethodGet, fmt.Sprintf("chats/%d/members/me", chatID), values, nil)
if err != nil {
@ -72,8 +74,8 @@ func (a *chats) GetChatMembership(chatID int) (*ChatMember, error) {
}
//GetChatMembers returns users participated in chat
func (a *chats) GetChatMembers(chatID, count, marker int) (*ChatMembersList, error) {
result := new(ChatMembersList)
func (a *chats) GetChatMembers(chatID, count, marker int) (*schemes.ChatMembersList, error) {
result := new(schemes.ChatMembersList)
values := url.Values{}
if count > 0 {
values.Set("count", strconv.Itoa(int(count)))
@ -94,8 +96,8 @@ func (a *chats) GetChatMembers(chatID, count, marker int) (*ChatMembersList, err
}
//LeaveChat removes bot from chat members
func (a *chats) LeaveChat(chatID int) (*SimpleQueryResult, error) {
result := new(SimpleQueryResult)
func (a *chats) LeaveChat(chatID int) (*schemes.SimpleQueryResult, error) {
result := new(schemes.SimpleQueryResult)
values := url.Values{}
body, err := a.client.request(http.MethodDelete, fmt.Sprintf("chats/%d/members/me", chatID), values, nil)
if err != nil {
@ -110,8 +112,8 @@ func (a *chats) LeaveChat(chatID int) (*SimpleQueryResult, error) {
}
//EditChat edits chat info: title, icon, etc…
func (a *chats) EditChat(chatID int, update *ChatPatch) (*Chat, error) {
result := new(Chat)
func (a *chats) EditChat(chatID int, update *schemes.ChatPatch) (*schemes.Chat, error) {
result := new(schemes.Chat)
values := url.Values{}
body, err := a.client.request(http.MethodPatch, fmt.Sprintf("chats/%d", chatID), values, update)
if err != nil {
@ -126,8 +128,8 @@ func (a *chats) EditChat(chatID int, update *ChatPatch) (*Chat, error) {
}
//AddMember adds members to chat. Additional permissions may require.
func (a *chats) AddMember(chatID int, users UserIdsList) (*SimpleQueryResult, error) {
result := new(SimpleQueryResult)
func (a *chats) AddMember(chatID int, users schemes.UserIdsList) (*schemes.SimpleQueryResult, error) {
result := new(schemes.SimpleQueryResult)
values := url.Values{}
body, err := a.client.request(http.MethodPost, fmt.Sprintf("chats/%d/members", chatID), values, users)
if err != nil {
@ -142,8 +144,8 @@ func (a *chats) AddMember(chatID int, users UserIdsList) (*SimpleQueryResult, er
}
//RemoveMember removes member from chat. Additional permissions may require.
func (a *chats) RemoveMember(chatID int, userID int) (*SimpleQueryResult, error) {
result := new(SimpleQueryResult)
func (a *chats) RemoveMember(chatID int, userID int) (*schemes.SimpleQueryResult, error) {
result := new(schemes.SimpleQueryResult)
values := url.Values{}
values.Set("user_id", strconv.Itoa(int(userID)))
body, err := a.client.request(http.MethodDelete, fmt.Sprintf("chats/%d/members", chatID), values, nil)
@ -159,10 +161,10 @@ func (a *chats) RemoveMember(chatID int, userID int) (*SimpleQueryResult, error)
}
//SendAction send bot action to chat
func (a *chats) SendAction(chatID int, action SenderAction) (*SimpleQueryResult, error) {
result := new(SimpleQueryResult)
func (a *chats) SendAction(chatID int, action schemes.SenderAction) (*schemes.SimpleQueryResult, error) {
result := new(schemes.SimpleQueryResult)
values := url.Values{}
body, err := a.client.request(http.MethodPost, fmt.Sprintf("chats/%d/actions", chatID), values, ActionRequestBody{Action: action})
body, err := a.client.request(http.MethodPost, fmt.Sprintf("chats/%d/actions", chatID), values, schemes.ActionRequestBody{Action: action})
if err != nil {
return result, err
}

View file

@ -6,6 +6,8 @@ import (
"io"
"net/http"
"net/url"
"github.com/neonxp/tamtam/schemes"
)
type client struct {
@ -42,7 +44,7 @@ func (cl *client) requestReader(method, path string, query url.Values, body io.R
return nil, err
}
if resp.StatusCode != http.StatusOK {
errObj := new(Error)
errObj := new(schemes.Error)
err = json.NewDecoder(resp.Body).Decode(errObj)
if err != nil {
return nil, err

View file

@ -2,12 +2,12 @@ package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"github.com/neonxp/tamtam"
"github.com/neonxp/tamtam/schemes"
)
func main() {
@ -25,7 +25,7 @@ func main() {
for upd := range api.GetUpdates(ctx) { // Чтение из канала с обновлениями
log.Printf("Received: %#v", upd)
switch upd := upd.(type) { // Определение типа пришедшего обновления
case *tamtam.MessageCreatedUpdate:
case *schemes.MessageCreatedUpdate:
// Создание клавиатуры
keyboard := api.Messages.NewKeyboardBuilder()
keyboard.
@ -34,46 +34,27 @@ func main() {
AddContact("Прислать контакт")
keyboard.
AddRow().
AddLink("Библиотека", tamtam.POSITIVE, "https://github.com/neonxp/tamtam").
AddCallback("Колбек 1", tamtam.NEGATIVE, "callback_1").
AddCallback("Колбек 2", tamtam.NEGATIVE, "callback_2")
AddLink("Библиотека", schemes.POSITIVE, "https://github.com/neonxp/tamtam").
AddCallback("Колбек 1", schemes.NEGATIVE, "callback_1").
AddCallback("Колбек 2", schemes.NEGATIVE, "callback_2")
keyboard.
AddRow().
AddCallback("Картинка", tamtam.POSITIVE, "picture")
AddCallback("Картинка", schemes.POSITIVE, "picture")
// Отправка сообщения с клавиатурой
res, err := api.Messages.SendMessage(0, upd.Message.Sender.UserId, &tamtam.NewMessageBody{
Text: fmt.Sprintf("Hello, %s! Your message: %s", upd.Message.Sender.Name, upd.Message.Body.Text),
Attachments: []interface{}{
tamtam.NewInlineKeyboardAttachmentRequest(keyboard.Build()),
},
})
log.Printf("Answer: %#v %#v", res, err)
case *tamtam.MessageCallbackUpdate:
err := api.Messages.Send(tamtam.NewMessage().SetUser(upd.Message.Sender.UserId).AddKeyboard(keyboard))
log.Printf("Answer: %#v", err)
case *schemes.MessageCallbackUpdate:
// Ответ на коллбек
attachments := make([]interface{}, 0)
if upd.Callback.Payload == "picture" {
photo, err := api.Uploads.UploadPhoto("./examples/example.jpg")
photo, err := api.Uploads.UploadPhotoFromFile("./examples/example.jpg")
if err != nil {
log.Fatal(err)
}
attachments = append(attachments, tamtam.NewPhotoAttachmentRequest(tamtam.PhotoAttachmentRequestPayload{Photos: photo.Photos}))
if err := api.Messages.Send(tamtam.NewMessage().SetUser(upd.Message.Sender.UserId).AddPhoto(photo)); err != nil {
log.Fatal(err)
}
}
res, err := api.Messages.AnswerOnCallback(
upd.Callback.CallbackID,
&tamtam.CallbackAnswer{
UserId: upd.Callback.User.UserId,
Message: &tamtam.NewMessageBody{
Text: "OK!",
},
Notification: "Callback is ok",
})
log.Printf("Answer: %#v %#v", res, err)
res2, err := api.Messages.SendMessage(0, upd.Callback.User.UserId, &tamtam.NewMessageBody{
Text: upd.Callback.Payload + " at " + upd.GetUpdateTime().String(),
Attachments: attachments,
})
log.Printf("Answer: %#v %#v", res2, err)
default:
log.Printf("Unknown type: %#v", upd)
}

View file

@ -29,9 +29,11 @@ func main() {
log.Printf("Received: %#v", upd)
switch upd := upd.(type) {
case *tamtam.MessageCreatedUpdate:
res, err := api.Messages.SendMessage(0, upd.Message.Sender.UserId, &tamtam.NewMessageBody{
Text: fmt.Sprintf("Hello, %s! Your message: %s", upd.Message.Sender.Name, upd.Message.Body.Text),
})
err := api.Messages.Send(
tamtam.NewMessage().
SetUser(upd.Message.Sender.UserId).
SetText(fmt.Sprintf("Hello, %s! Your message: %s", upd.Message.Sender.Name, upd.Message.Body.Text)),
)
log.Printf("Answer: %#v %#v", res, err)
default:
log.Printf("Unknown type: %#v", upd)

View file

@ -39,10 +39,12 @@ func main() {
log.Printf("Received: %#v", upd)
switch upd := upd.(type) {
case tamtam.MessageCreatedUpdate:
res, err := api.Messages.SendMessage(0, upd.Message.Sender.UserId, &tamtam.NewMessageBody{
Text: fmt.Sprintf("Hello, %s! Your message: %s", upd.Message.Sender.Name, upd.Message.Body.Text),
})
log.Printf("Answer: %#v %#v", res, err)
err := api.Messages.Send(
tamtam.NewMessage().
SetUser(upd.Message.Sender.UserId).
SetText(fmt.Sprintf("Hello, %s! Your message: %s", upd.Message.Sender.Name, upd.Message.Body.Text)),
)
log.Printf("Answer: %#v", err)
default:
log.Printf("Unknown type: %#v", upd)
}

View file

@ -1,43 +1,45 @@
package tamtam
//KeyboardBuilder implements builder for inline keyboard
type KeyboardBuilder struct {
import "github.com/neonxp/tamtam/schemes"
//Keyboard implements builder for inline keyboard
type Keyboard struct {
rows []*KeyboardRow
}
//AddRow adds row to inline keyboard
func (k *KeyboardBuilder) AddRow() *KeyboardRow {
func (k *Keyboard) AddRow() *KeyboardRow {
kr := &KeyboardRow{}
k.rows = append(k.rows, kr)
return kr
}
//Build returns result keyboard
func (k *KeyboardBuilder) Build() Keyboard {
buttons := make([][]ButtonInterface, 0, len(k.rows))
func (k *Keyboard) Build() schemes.Keyboard {
buttons := make([][]schemes.ButtonInterface, 0, len(k.rows))
for _, r := range k.rows {
buttons = append(buttons, r.Build())
}
return Keyboard{Buttons: buttons}
return schemes.Keyboard{Buttons: buttons}
}
//KeyboardRow represents buttons row
type KeyboardRow struct {
cols []ButtonInterface
cols []schemes.ButtonInterface
}
//Build returns result keyboard row
func (k *KeyboardRow) Build() []ButtonInterface {
func (k *KeyboardRow) Build() []schemes.ButtonInterface {
return k.cols
}
//AddLink button
func (k *KeyboardRow) AddLink(text string, intent Intent, url string) *KeyboardRow {
b := LinkButton{
func (k *KeyboardRow) AddLink(text string, intent schemes.Intent, url string) *KeyboardRow {
b := schemes.LinkButton{
Url: url,
Button: Button{
Button: schemes.Button{
Text: text,
Type: LINK,
Type: schemes.LINK,
},
}
k.cols = append(k.cols, b)
@ -45,13 +47,13 @@ func (k *KeyboardRow) AddLink(text string, intent Intent, url string) *KeyboardR
}
//AddCallback button
func (k *KeyboardRow) AddCallback(text string, intent Intent, payload string) *KeyboardRow {
b := CallbackButton{
func (k *KeyboardRow) AddCallback(text string, intent schemes.Intent, payload string) *KeyboardRow {
b := schemes.CallbackButton{
Payload: payload,
Intent: intent,
Button: Button{
Button: schemes.Button{
Text: text,
Type: CALLBACK,
Type: schemes.CALLBACK,
},
}
k.cols = append(k.cols, b)
@ -60,10 +62,10 @@ func (k *KeyboardRow) AddCallback(text string, intent Intent, payload string) *K
//AddContact button
func (k *KeyboardRow) AddContact(text string) *KeyboardRow {
b := RequestContactButton{
Button: Button{
b := schemes.RequestContactButton{
Button: schemes.Button{
Text: text,
Type: CONTACT,
Type: schemes.CONTACT,
},
}
k.cols = append(k.cols, b)
@ -72,11 +74,11 @@ func (k *KeyboardRow) AddContact(text string) *KeyboardRow {
//AddGeolocation button
func (k *KeyboardRow) AddGeolocation(text string, quick bool) *KeyboardRow {
b := RequestGeoLocationButton{
b := schemes.RequestGeoLocationButton{
Quick: quick,
Button: Button{
Button: schemes.Button{
Text: text,
Type: GEOLOCATION,
Type: schemes.GEOLOCATION,
},
}
k.cols = append(k.cols, b)

81
message.go Normal file
View file

@ -0,0 +1,81 @@
package tamtam
import "github.com/neonxp/tamtam/schemes"
type Message struct {
userID int
chatID int
message *schemes.NewMessageBody
}
func NewMessage() *Message {
return &Message{userID: 0, chatID: 0, message: &schemes.NewMessageBody{Attachments: []interface{}{}}}
}
func (m *Message) SetUser(userID int) *Message {
m.userID = userID
return m
}
func (m *Message) SetChat(chatID int) *Message {
m.chatID = chatID
return m
}
func (m *Message) SetText(text string) *Message {
m.message.Text = text
return m
}
func (m *Message) SetNotify(notify bool) *Message {
m.message.Notify = notify
return m
}
func (m *Message) AddKeyboard(keyboard *Keyboard) *Message {
m.message.Attachments = append(m.message.Attachments, schemes.NewInlineKeyboardAttachmentRequest(keyboard.Build()))
return m
}
func (m *Message) AddPhoto(photo *schemes.PhotoTokens) *Message {
m.message.Attachments = append(m.message.Attachments, schemes.NewPhotoAttachmentRequest(schemes.PhotoAttachmentRequestPayload{
Photos: photo.Photos,
}))
return m
}
func (m *Message) AddAudio(audio *schemes.UploadedInfo) *Message {
m.message.Attachments = append(m.message.Attachments, schemes.NewAudioAttachmentRequest(*audio))
return m
}
func (m *Message) AddVideo(video *schemes.UploadedInfo) *Message {
m.message.Attachments = append(m.message.Attachments, schemes.NewVideoAttachmentRequest(*video))
return m
}
func (m *Message) AddFile(file *schemes.UploadedInfo) *Message {
m.message.Attachments = append(m.message.Attachments, schemes.NewFileAttachmentRequest(*file))
return m
}
func (m *Message) AddLocation(lat float64, lon float64) *Message {
m.message.Attachments = append(m.message.Attachments, schemes.NewLocationAttachmentRequest(lat, lon))
return m
}
func (m *Message) AddContact(name string, contactID int, vcfInfo string, vcfPhone string) *Message {
m.message.Attachments = append(m.message.Attachments, schemes.NewContactAttachmentRequest(schemes.ContactAttachmentRequestPayload{
Name: name,
ContactId: contactID,
VcfInfo: vcfInfo,
VcfPhone: vcfPhone,
}))
return m
}
func (m *Message) AddSticker(code string) *Message {
m.message.Attachments = append(m.message.Attachments, schemes.NewStickerAttachmentRequest(schemes.StickerAttachmentRequestPayload{
Code: code,
}))
return m
}

View file

@ -2,10 +2,13 @@ package tamtam
import (
"encoding/json"
"errors"
"log"
"net/http"
"net/url"
"strconv"
"github.com/neonxp/tamtam/schemes"
)
type messages struct {
@ -17,8 +20,8 @@ func newMessages(client *client) *messages {
}
//GetMessages returns messages in chat: result page and marker referencing to the next page. Messages traversed in reverse direction so the latest message in chat will be first in result array. Therefore if you use from and to parameters, to must be less than from
func (a *messages) GetMessages(chatID int, messageIDs []string, from int, to int, count int) (*MessageList, error) {
result := new(MessageList)
func (a *messages) GetMessages(chatID int, messageIDs []string, from int, to int, count int) (*schemes.MessageList, error) {
result := new(schemes.MessageList)
values := url.Values{}
if chatID != 0 {
values.Set("chat_id", strconv.Itoa(int(chatID)))
@ -49,48 +52,21 @@ func (a *messages) GetMessages(chatID int, messageIDs []string, from int, to int
return result, json.NewDecoder(body).Decode(result)
}
//SendMessage sends a message to a chat. As a result for this method new message identifier returns.
func (a *messages) SendMessage(chatID int, userID int, message *NewMessageBody) (*Message, error) {
result := new(Message)
values := url.Values{}
if chatID != 0 {
values.Set("chat_id", strconv.Itoa(int(chatID)))
}
if userID != 0 {
values.Set("user_id", strconv.Itoa(int(userID)))
}
body, err := a.client.request(http.MethodPost, "messages", values, message)
if err != nil {
return result, err
}
defer func() {
if err := body.Close(); err != nil {
log.Println(err)
}
}()
return result, json.NewDecoder(body).Decode(result)
}
//EditMessage updates message by id
func (a *messages) EditMessage(messageID int, message *NewMessageBody) (*SimpleQueryResult, error) {
result := new(SimpleQueryResult)
values := url.Values{}
values.Set("message_id", strconv.Itoa(int(messageID)))
body, err := a.client.request(http.MethodPut, "messages", values, message)
func (a *messages) EditMessage(messageID int, message *Message) error {
s, err := a.editMessage(messageID, message.message)
if err != nil {
return result, err
return err
}
defer func() {
if err := body.Close(); err != nil {
log.Println(err)
if !s.Success {
return errors.New(s.Message)
}
}()
return result, json.NewDecoder(body).Decode(result)
return nil
}
//DeleteMessage deletes message by id
func (a *messages) DeleteMessage(messageID int) (*SimpleQueryResult, error) {
result := new(SimpleQueryResult)
func (a *messages) DeleteMessage(messageID int) (*schemes.SimpleQueryResult, error) {
result := new(schemes.SimpleQueryResult)
values := url.Values{}
values.Set("message_id", strconv.Itoa(int(messageID)))
body, err := a.client.request(http.MethodDelete, "messages", values, nil)
@ -106,8 +82,8 @@ func (a *messages) DeleteMessage(messageID int) (*SimpleQueryResult, error) {
}
//AnswerOnCallback should be called to send an answer after a user has clicked the button. The answer may be an updated message or/and a one-time user notification.
func (a *messages) AnswerOnCallback(callbackID string, callback *CallbackAnswer) (*SimpleQueryResult, error) {
result := new(SimpleQueryResult)
func (a *messages) AnswerOnCallback(callbackID string, callback *schemes.CallbackAnswer) (*schemes.SimpleQueryResult, error) {
result := new(schemes.SimpleQueryResult)
values := url.Values{}
values.Set("callback_id", callbackID)
body, err := a.client.request(http.MethodPost, "answers", values, callback)
@ -123,8 +99,52 @@ func (a *messages) AnswerOnCallback(callbackID string, callback *CallbackAnswer)
}
//NewKeyboardBuilder returns new keyboard builder helper
func (a *messages) NewKeyboardBuilder() *KeyboardBuilder {
return &KeyboardBuilder{
func (a *messages) NewKeyboardBuilder() *Keyboard {
return &Keyboard{
rows: make([]*KeyboardRow, 0),
}
}
//Send sends a message to a chat. As a result for this method new message identifier returns.
func (a *messages) Send(m *Message) error {
return a.sendMessage(m.chatID, m.userID, m.message)
}
func (a *messages) sendMessage(chatID int, userID int, message *schemes.NewMessageBody) error {
result := new(schemes.Error)
values := url.Values{}
if chatID != 0 {
values.Set("chat_id", strconv.Itoa(int(chatID)))
}
if userID != 0 {
values.Set("user_id", strconv.Itoa(int(userID)))
}
body, err := a.client.request(http.MethodPost, "messages", values, message)
if err != nil {
return err
}
defer body.Close()
if err := json.NewDecoder(body).Decode(result); err != nil {
return err
}
if result.Code == "" {
return nil
}
return result
}
func (a *messages) editMessage(messageID int, message *schemes.NewMessageBody) (*schemes.SimpleQueryResult, error) {
result := new(schemes.SimpleQueryResult)
values := url.Values{}
values.Set("message_id", strconv.Itoa(int(messageID)))
body, err := a.client.request(http.MethodPut, "messages", values, message)
if err != nil {
return result, err
}
defer func() {
if err := body.Close(); err != nil {
log.Println(err)
}
}()
return result, json.NewDecoder(body).Decode(result)
}

View file

@ -1,4 +1,4 @@
package tamtam
package schemes
import (
"encoding/json"

View file

@ -5,6 +5,8 @@ import (
"log"
"net/http"
"net/url"
"github.com/neonxp/tamtam/schemes"
)
type subscriptions struct {
@ -16,8 +18,8 @@ func newSubscriptions(client *client) *subscriptions {
}
//GetSubscriptions returns list of all subscriptions
func (a *subscriptions) GetSubscriptions() (*GetSubscriptionsResult, error) {
result := new(GetSubscriptionsResult)
func (a *subscriptions) GetSubscriptions() (*schemes.GetSubscriptionsResult, error) {
result := new(schemes.GetSubscriptionsResult)
values := url.Values{}
body, err := a.client.request(http.MethodGet, "subscriptions", values, nil)
if err != nil {
@ -32,13 +34,13 @@ func (a *subscriptions) GetSubscriptions() (*GetSubscriptionsResult, error) {
}
//Subscribe subscribes bot to receive updates via WebHook
func (a *subscriptions) Subscribe(subscribeURL string, updateTypes []string) (*SimpleQueryResult, error) {
subscription := &SubscriptionRequestBody{
func (a *subscriptions) Subscribe(subscribeURL string, updateTypes []string) (*schemes.SimpleQueryResult, error) {
subscription := &schemes.SubscriptionRequestBody{
Url: subscribeURL,
UpdateTypes: updateTypes,
Version: a.client.version,
}
result := new(SimpleQueryResult)
result := new(schemes.SimpleQueryResult)
values := url.Values{}
body, err := a.client.request(http.MethodPost, "subscriptions", values, subscription)
if err != nil {
@ -53,8 +55,8 @@ func (a *subscriptions) Subscribe(subscribeURL string, updateTypes []string) (*S
}
//Unsubscribe unsubscribes bot from receiving updates via WebHook
func (a *subscriptions) Unsubscribe(subscriptionURL string) (*SimpleQueryResult, error) {
result := new(SimpleQueryResult)
func (a *subscriptions) Unsubscribe(subscriptionURL string) (*schemes.SimpleQueryResult, error) {
result := new(schemes.SimpleQueryResult)
values := url.Values{}
values.Set("url", subscriptionURL)
body, err := a.client.request(http.MethodDelete, "subscriptions", values, nil)

View file

@ -9,7 +9,8 @@ import (
"net/http"
"net/url"
"os"
"path"
"github.com/neonxp/tamtam/schemes"
)
type uploads struct {
@ -20,9 +21,61 @@ func newUploads(client *client) *uploads {
return &uploads{client: client}
}
//GetUploadURL returns url to upload files
func (a *uploads) GetUploadURL(uploadType UploadType) (*UploadEndpoint, error) {
result := new(UploadEndpoint)
//UploadMedia uploads file to TamTam server
func (a *uploads) UploadMediaFromFile(uploadType schemes.UploadType, filename string) (*schemes.UploadedInfo, error) {
fh, err := os.Open(filename)
if err != nil {
return nil, err
}
defer fh.Close()
return a.UploadMediaFromReader(uploadType, fh)
}
//UploadMediaFromUrl uploads file from remote server to TamTam server
func (a *uploads) UploadMediaFromUrl(uploadType schemes.UploadType, u url.URL) (*schemes.UploadedInfo, error) {
respFile, err := http.Get(u.String())
if err != nil {
return nil, err
}
defer respFile.Body.Close()
return a.UploadMediaFromReader(uploadType, respFile.Body)
}
func (a *uploads) UploadMediaFromReader(uploadType schemes.UploadType, reader io.Reader) (*schemes.UploadedInfo, error) {
result := new(schemes.UploadedInfo)
return result, a.uploadMediaFromReader(uploadType, reader, result)
}
//UploadPhotoFromFile uploads photos to TamTam server
func (a *uploads) UploadPhotoFromFile(filename string) (*schemes.PhotoTokens, error) {
fh, err := os.Open(filename)
if err != nil {
return nil, err
}
defer fh.Close()
result := new(schemes.PhotoTokens)
return result, a.uploadMediaFromReader(schemes.PHOTO, fh, result)
}
//UploadPhotoFromUrl uploads photo from remote server to TamTam server
func (a *uploads) UploadPhotoFromUrl(u url.URL) (*schemes.PhotoTokens, error) {
respFile, err := http.Get(u.String())
if err != nil {
return nil, err
}
defer respFile.Body.Close()
result := new(schemes.PhotoTokens)
return result, a.uploadMediaFromReader(schemes.PHOTO, respFile.Body, result)
}
//UploadPhotoFromReader uploads photo from reader
func (a *uploads) UploadPhotoFromReader(reader io.Reader) (*schemes.PhotoTokens, error) {
result := new(schemes.PhotoTokens)
return result, a.uploadMediaFromReader(schemes.PHOTO, reader, result)
}
func (a *uploads) getUploadURL(uploadType schemes.UploadType) (*schemes.UploadEndpoint, error) {
result := new(schemes.UploadEndpoint)
values := url.Values{}
values.Set("type", string(uploadType))
body, err := a.client.request(http.MethodPost, "uploads", values, nil)
@ -37,128 +90,37 @@ func (a *uploads) GetUploadURL(uploadType UploadType) (*UploadEndpoint, error) {
return result, json.NewDecoder(body).Decode(result)
}
//UploadMedia uploads file to TamTam server
func (a *uploads) UploadMedia(endpoint *UploadEndpoint, filename string) (*UploadedInfo, error) {
func (a *uploads) uploadMediaFromReader(uploadType schemes.UploadType, reader io.Reader, result interface{}) error {
endpoint, err := a.getUploadURL(uploadType)
if err != nil {
return err
}
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
fileWriter, err := bodyWriter.CreateFormFile("data", filename)
fileWriter, err := bodyWriter.CreateFormFile("data", "file")
if err != nil {
return nil, err
return err
}
fh, err := os.Open(filename)
_, err = io.Copy(fileWriter, reader)
if err != nil {
return nil, err
}
defer func() {
if err := fh.Close(); err != nil {
log.Println(err)
}
}()
_, err = io.Copy(fileWriter, fh)
if err != nil {
return nil, err
return err
}
if err := bodyWriter.Close(); err != nil {
return nil, err
return err
}
contentType := bodyWriter.FormDataContentType()
if err := bodyWriter.Close(); err != nil {
return err
}
resp, err := http.Post(endpoint.Url, contentType, bodyBuf)
if err != nil {
return nil, err
return err
}
defer func() {
if err := resp.Body.Close(); err != nil {
log.Println(err)
}
}()
result := new(UploadedInfo)
return result, json.NewDecoder(resp.Body).Decode(result)
}
//UploadMediaFromUrl uploads file from remote server to TamTam server
func (a *uploads) UploadMediaFromUrl(endpoint *UploadEndpoint, u url.URL) (*UploadedInfo, error) {
respFile, err := http.Get(u.String())
if err != nil {
return nil, err
}
defer respFile.Body.Close()
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
fileWriter, err := bodyWriter.CreateFormFile("data", path.Base(u.Path))
if err != nil {
return nil, err
}
_, err = io.Copy(fileWriter, respFile.Body)
if err != nil {
return nil, err
}
if err := bodyWriter.Close(); err != nil {
return nil, err
}
contentType := bodyWriter.FormDataContentType()
if err := bodyWriter.Close(); err != nil {
return nil, err
}
resp, err := http.Post(endpoint.Url, contentType, bodyBuf)
if err != nil {
return nil, err
}
defer func() {
if err := resp.Body.Close(); err != nil {
log.Println(err)
}
}()
result := new(UploadedInfo)
return result, json.NewDecoder(resp.Body).Decode(result)
}
//UploadPhoto uploads photos to TamTam server
func (a *uploads) UploadPhoto(filename string) (*PhotoTokens, error) {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
fileWriter, err := bodyWriter.CreateFormFile("data", filename)
if err != nil {
return nil, err
}
fh, err := os.Open(filename)
if err != nil {
return nil, err
}
defer func() {
if err := fh.Close(); err != nil {
log.Println(err)
}
}()
_, err = io.Copy(fileWriter, fh)
if err != nil {
return nil, err
}
if err := bodyWriter.Close(); err != nil {
return nil, err
}
endpoint, err := a.GetUploadURL(PHOTO)
if err != nil {
return nil, err
}
contentType := bodyWriter.FormDataContentType()
resp, err := http.Post(endpoint.Url, contentType, bodyBuf)
if err != nil {
return nil, err
}
defer func() {
if err := resp.Body.Close(); err != nil {
log.Println(err)
}
}()
result := new(PhotoTokens)
return result, json.NewDecoder(resp.Body).Decode(result)
return json.NewDecoder(resp.Body).Decode(result)
}