aboutsummaryrefslogtreecommitdiff
path: root/api.go
diff options
context:
space:
mode:
Diffstat (limited to 'api.go')
-rw-r--r--api.go582
1 files changed, 117 insertions, 465 deletions
diff --git a/api.go b/api.go
index fdc8a24..57ee096 100644
--- a/api.go
+++ b/api.go
@@ -1,499 +1,146 @@
-/*
- * TamTam Bot API
- */
+// Package tamtam implements TamTam Bot API
+// Copyright (c) 2019 Alexander Kiryukhin <a.kiryukhin@mail.ru>
package tamtam
import (
- "bytes"
"context"
"encoding/json"
- "fmt"
- "io"
"io/ioutil"
"log"
- "mime/multipart"
"net/http"
"net/url"
- "os"
"strconv"
"time"
)
type Api struct {
- key string
- version string
- url *url.URL
- timeout int
- pause int
- logging bool
+ Bots *bots
+ Chats *chats
+ Messages *messages
+ Subscriptions *subscriptions
+ Uploads *uploads
+ client *client
+ updates chan UpdateInterface
+ timeout int
+ pause int
}
// New TamTam Api object
func New(key string) *Api {
u, _ := url.Parse("https://botapi.tamtam.chat/")
+ cl := newClient(key, "0.1.8", u)
return &Api{
- key: key,
- url: u,
- version: "0.1.8",
- timeout: 30,
- pause: 1,
- logging: false,
+ Bots: newBots(cl),
+ Chats: newChats(cl),
+ Uploads: newUploads(cl),
+ Messages: newMessages(cl),
+ Subscriptions: newSubscriptions(cl),
+ client: cl,
+ updates: make(chan UpdateInterface),
+ timeout: 30,
+ pause: 1,
}
}
-func (a *Api) EnableLogging() {
- a.logging = true
-}
-
-// region Misc methods
-
-func (a *Api) GetMe() (*UserWithPhoto, error) {
- result := new(UserWithPhoto)
- values := url.Values{}
- body, err := a.request(http.MethodGet, "me", values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) GetUploadURL(uploadType UploadType) (*UploadEndpoint, error) {
- result := new(UploadEndpoint)
- values := url.Values{}
- values.Set("type", string(uploadType))
- body, err := a.request(http.MethodPost, "uploads", values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) UploadMedia(uploadType UploadType, filename string) (*UploadedInfo, error) {
- bodyBuf := &bytes.Buffer{}
- bodyWriter := multipart.NewWriter(bodyBuf)
-
- // this step is very important
- fileWriter, err := bodyWriter.CreateFormFile("data", filename)
- if err != nil {
- return nil, err
- }
-
- // open file handle
- fh, err := os.Open(filename)
- if err != nil {
- return nil, err
- }
- defer fh.Close()
-
- //iocopy
- _, err = io.Copy(fileWriter, fh)
- if err != nil {
- return nil, err
- }
-
- if err := bodyWriter.Close(); err != nil {
- return nil, err
- }
-
- target, err := a.GetUploadURL(uploadType)
- if err != nil {
- return nil, err
- }
- contentType := bodyWriter.FormDataContentType()
-
- resp, err := http.Post(target.Url, contentType, bodyBuf)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- result := new(UploadedInfo)
- return result, json.NewDecoder(resp.Body).Decode(result)
-}
-
-func (a *Api) GetUpdatesLoop(ctx context.Context, updates chan interface{}) error {
- for {
- select {
- case <-ctx.Done():
- return nil
- case <-time.After(time.Duration(a.pause) * time.Second):
- var marker int64
- for {
- upds, err := a.getUpdates(50, a.timeout, marker, []string{})
- if err != nil {
- return err
- }
- if len(upds.Updates) == 0 {
- break
- }
- for _, u := range upds.Updates {
- updates <- a.bytesToProperUpdate(u)
- }
- marker = upds.Marker
- }
- }
- }
-}
-
-func (a *Api) GetHandler(updates chan interface{}) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- defer r.Body.Close()
- b, _ := ioutil.ReadAll(r.Body)
- updates <- a.bytesToProperUpdate(b)
- }
-}
-
-// endregion
-
-// region Chat methods
-func (a *Api) GetChats(count, marker int64) (*ChatList, error) {
- result := new(ChatList)
- values := url.Values{}
- if count > 0 {
- values.Set("count", strconv.Itoa(int(count)))
- }
- if marker > 0 {
- values.Set("marker", strconv.Itoa(int(marker)))
- }
- body, err := a.request(http.MethodGet, "chats", values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) GetChat(chatID int64) (*Chat, error) {
- result := new(Chat)
- values := url.Values{}
- body, err := a.request(http.MethodGet, fmt.Sprintf("chats/%d", chatID), values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) GetChatMembership(chatID int64) (*ChatMember, error) {
- result := new(ChatMember)
- values := url.Values{}
- body, err := a.request(http.MethodGet, fmt.Sprintf("chats/%d/members/me", chatID), values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) GetChatMembers(chatID, count, marker int64) (*ChatMembersList, error) {
- result := new(ChatMembersList)
- values := url.Values{}
- if count > 0 {
- values.Set("count", strconv.Itoa(int(count)))
- }
- if marker > 0 {
- values.Set("marker", strconv.Itoa(int(marker)))
- }
- body, err := a.request(http.MethodGet, fmt.Sprintf("chats/%d/members", chatID), values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) LeaveChat(chatID int64) (*SimpleQueryResult, error) {
- result := new(SimpleQueryResult)
- values := url.Values{}
- body, err := a.request(http.MethodDelete, fmt.Sprintf("chats/%d/members/me", chatID), values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) EditChat(chatID int64, update *ChatPatch) (*Chat, error) {
- result := new(Chat)
- values := url.Values{}
- body, err := a.request(http.MethodPatch, fmt.Sprintf("chats/%d", chatID), values, update)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) AddMember(chatID int64, users UserIdsList) (*SimpleQueryResult, error) {
- result := new(SimpleQueryResult)
- values := url.Values{}
- body, err := a.request(http.MethodPost, fmt.Sprintf("chats/%d/members", chatID), values, users)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) RemoveMember(chatID int64, userID int64) (*SimpleQueryResult, error) {
- result := new(SimpleQueryResult)
- values := url.Values{}
- values.Set("user_id", strconv.Itoa(int(userID)))
- body, err := a.request(http.MethodDelete, fmt.Sprintf("chats/%d/members", chatID), values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) SendAction(chatID int64, action SenderAction) (*SimpleQueryResult, error) {
- result := new(SimpleQueryResult)
- values := url.Values{}
- body, err := a.request(http.MethodPost, fmt.Sprintf("chats/%d/actions", chatID), values, ActionRequestBody{Action: action})
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-// endregion
-
-// region Message methods
-
-func (a *Api) GetMessages(chatID int64, messageIDs []string, from int64, to int64, count int64) (*MessageList, error) {
- result := new(MessageList)
- values := url.Values{}
- if chatID != 0 {
- values.Set("chat_id", strconv.Itoa(int(chatID)))
- }
- if len(messageIDs) > 0 {
- for _, mid := range messageIDs {
- values.Add("message_ids", mid)
- }
- }
- if from != 0 {
- values.Set("from", strconv.Itoa(int(from)))
- }
- if to != 0 {
- values.Set("to", strconv.Itoa(int(to)))
- }
- if count > 0 {
- values.Set("count", strconv.Itoa(int(count)))
- }
- body, err := a.request(http.MethodGet, "messages", values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) SendMessage(chatID int64, userID int64, 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.request(http.MethodPost, "messages", values, message)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) EditMessage(messageID int64, message *NewMessageBody) (*SimpleQueryResult, error) {
- result := new(SimpleQueryResult)
- values := url.Values{}
- values.Set("message_id", strconv.Itoa(int(messageID)))
- body, err := a.request(http.MethodPut, "messages", values, message)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) DeleteMessage(messageID int64) (*SimpleQueryResult, error) {
- result := new(SimpleQueryResult)
- values := url.Values{}
- values.Set("message_id", strconv.Itoa(int(messageID)))
- body, err := a.request(http.MethodDelete, "messages", values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) AnswerOnCallback(callbackID int64, callback *CallbackAnswer) (*SimpleQueryResult, error) {
- result := new(SimpleQueryResult)
- values := url.Values{}
- values.Set("callback_id", strconv.Itoa(int(callbackID)))
- body, err := a.request(http.MethodPost, "answers", values, callback)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-// endregion
-
-// region Subscriptions
-
-func (a *Api) GetSubscriptions() (*GetSubscriptionsResult, error) {
- result := new(GetSubscriptionsResult)
- values := url.Values{}
- body, err := a.request(http.MethodGet, "subscriptions", values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) Subscribe(subscribeURL string, updateTypes []string) (*SimpleQueryResult, error) {
- subscription := &SubscriptionRequestBody{
- Url: subscribeURL,
- UpdateTypes: updateTypes,
- Version: a.version,
- }
- result := new(SimpleQueryResult)
- values := url.Values{}
- body, err := a.request(http.MethodPost, "subscriptions", values, subscription)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-func (a *Api) Unsubscribe(subscriptionURL string) (*SimpleQueryResult, error) {
- result := new(SimpleQueryResult)
- values := url.Values{}
- values.Set("url", subscriptionURL)
- body, err := a.request(http.MethodDelete, "subscriptions", values, nil)
- if err != nil {
- return result, err
- }
- defer body.Close()
- return result, json.NewDecoder(body).Decode(result)
-}
-
-// endregion
-
-// region Internal
-
-func (a *Api) bytesToProperUpdate(b []byte) interface{} {
+func (a *Api) bytesToProperUpdate(b []byte) UpdateInterface {
u := new(Update)
_ = json.Unmarshal(b, u)
- switch u.UpdateType {
- case UpdateTypeMessageCallback:
- upd := UpdateMessageCallback{}
- _ = json.Unmarshal(b, &upd)
+ switch u.GetUpdateType() {
+ case TypeMessageCallback:
+ upd := new(MessageCallbackUpdate)
+ _ = json.Unmarshal(b, upd)
return upd
- case UpdateTypeMessageCreated:
- upd := UpdateMessageCreated{}
- _ = json.Unmarshal(b, &upd)
- for _, att := range upd.Message.Body.RawAttachments {
+ case TypeMessageCreated:
+ upd := new(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 UpdateTypeMessageRemoved:
- upd := UpdateMessageRemoved{}
- _ = json.Unmarshal(b, &upd)
+ case TypeMessageRemoved:
+ upd := new(MessageRemovedUpdate)
+ _ = json.Unmarshal(b, upd)
return upd
- case UpdateTypeMessageEdited:
- upd := UpdateMessageEdited{}
- _ = json.Unmarshal(b, &upd)
- for _, att := range upd.Message.Body.RawAttachments {
+ case TypeMessageEdited:
+ upd := new(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 UpdateTypeMessageRestored:
- upd := UpdateMessageRestored{}
- _ = json.Unmarshal(b, &upd)
+ case TypeBotAdded:
+ upd := new(BotAddedToChatUpdate)
+ _ = json.Unmarshal(b, upd)
return upd
- case UpdateTypeBotAdded:
- upd := UpdateBotAdded{}
- _ = json.Unmarshal(b, &upd)
+ case TypeBotRemoved:
+ upd := new(BotRemovedFromChatUpdate)
+ _ = json.Unmarshal(b, upd)
return upd
- case UpdateTypeBotRemoved:
- upd := UpdateBotRemoved{}
- _ = json.Unmarshal(b, &upd)
+ case TypeUserAdded:
+ upd := new(UserAddedToChatUpdate)
+ _ = json.Unmarshal(b, upd)
return upd
- case UpdateTypeUserAdded:
- upd := UpdateUserAdded{}
- _ = json.Unmarshal(b, &upd)
+ case TypeUserRemoved:
+ upd := new(UserRemovedFromChatUpdate)
+ _ = json.Unmarshal(b, upd)
return upd
- case UpdateTypeUserRemoved:
- upd := UpdateUserRemoved{}
- _ = json.Unmarshal(b, &upd)
+ case TypeBotStarted:
+ upd := new(BotStartedUpdate)
+ _ = json.Unmarshal(b, upd)
return upd
- case UpdateTypeBotStarted:
- upd := UpdateBotStarted{}
- _ = json.Unmarshal(b, &upd)
- return upd
- case UpdateTypeChatTitleChanged:
- upd := UpdateChatTitleChanged{}
- _ = json.Unmarshal(b, &upd)
+ case TypeChatTitleChanged:
+ upd := new(ChatTitleChangedUpdate)
+ _ = json.Unmarshal(b, upd)
return upd
}
return nil
}
-func (a *Api) bytesToProperAttachment(b []byte) interface{} {
+func (a *Api) bytesToProperAttachment(b []byte) AttachmentInterface {
attachment := new(Attachment)
_ = json.Unmarshal(b, attachment)
- switch attachment.Type {
+ switch attachment.GetAttachmentType() {
case AttachmentAudio:
- res := &AudioAttachment{}
- _ = json.Unmarshal(b, &res)
+ res := new(AudioAttachment)
+ _ = json.Unmarshal(b, res)
return res
case AttachmentContact:
- res := &ContactAttachment{}
- _ = json.Unmarshal(b, &res)
+ res := new(ContactAttachment)
+ _ = json.Unmarshal(b, res)
return res
case AttachmentFile:
- res := &FileAttachment{}
- _ = json.Unmarshal(b, &res)
+ res := new(FileAttachment)
+ _ = json.Unmarshal(b, res)
return res
case AttachmentImage:
- res := &PhotoAttachment{}
- _ = json.Unmarshal(b, &res)
+ res := new(PhotoAttachment)
+ _ = json.Unmarshal(b, res)
return res
case AttachmentKeyboard:
- res := &InlineKeyboardAttachment{}
- _ = json.Unmarshal(b, &res)
+ res := new(InlineKeyboardAttachment)
+ _ = json.Unmarshal(b, res)
return res
case AttachmentLocation:
- res := &LocationAttachment{}
- _ = json.Unmarshal(b, &res)
+ res := new(LocationAttachment)
+ _ = json.Unmarshal(b, res)
return res
case AttachmentShare:
- res := &ShareAttachment{}
- _ = json.Unmarshal(b, &res)
+ res := new(ShareAttachment)
+ _ = json.Unmarshal(b, res)
return res
case AttachmentSticker:
- res := &StickerAttachment{}
- _ = json.Unmarshal(b, &res)
+ res := new(StickerAttachment)
+ _ = json.Unmarshal(b, res)
return res
case AttachmentVideo:
- res := &VideoAttachment{}
- _ = json.Unmarshal(b, &res)
+ res := new(VideoAttachment)
+ _ = json.Unmarshal(b, res)
return res
}
return attachment
}
-func (a *Api) getUpdates(limit int, timeout int, marker int64, types []string) (*UpdateList, error) {
+func (a *Api) getUpdates(limit int, timeout int, marker int, types []string) (*UpdateList, error) {
result := new(UpdateList)
values := url.Values{}
if limit > 0 {
@@ -503,57 +150,62 @@ func (a *Api) getUpdates(limit int, timeout int, marker int64, types []string) (
values.Set("timeout", strconv.Itoa(timeout))
}
if marker > 0 {
- values.Set("marker", strconv.Itoa(int(marker)))
+ values.Set("marker", strconv.Itoa(marker))
}
if len(types) > 0 {
for _, t := range types {
values.Add("types", t)
}
}
- body, err := a.request(http.MethodGet, "updates", values, nil)
+ body, err := a.client.request(http.MethodGet, "updates", values, nil)
if err != nil {
return result, err
}
- defer body.Close()
+ defer func() {
+ if err := body.Close(); err != nil {
+ log.Println(err)
+ }
+ }()
jb, _ := ioutil.ReadAll(body)
- if a.logging {
- log.Printf("Received: %s", string(jb))
- }
return result, json.Unmarshal(jb, result)
}
-func (a *Api) request(method, path string, query url.Values, body interface{}) (io.ReadCloser, error) {
- j, err := json.Marshal(body)
- if err != nil {
- return nil, err
+func (a *Api) UpdatesLoop(ctx context.Context) error {
+ for {
+ select {
+ case <-ctx.Done():
+ return nil
+ case <-time.After(time.Duration(a.pause) * time.Second):
+ var marker int
+ for {
+ upds, err := a.getUpdates(50, a.timeout, marker, []string{})
+ if err != nil {
+ return err
+ }
+ if len(upds.Updates) == 0 {
+ break
+ }
+ for _, u := range upds.Updates {
+ a.updates <- a.bytesToProperUpdate(u)
+ }
+ marker = *upds.Marker
+ }
+ }
}
- return a.requestReader(method, path, query, bytes.NewReader(j))
}
-func (a *Api) requestReader(method, path string, query url.Values, body io.Reader) (io.ReadCloser, error) {
- c := http.DefaultClient
- u := *a.url
- u.Path = path
- query.Set("access_token", a.key)
- query.Set("v", a.version)
- u.RawQuery = query.Encode()
- if a.logging {
- log.Printf("Sent: [%s %s] Query: %#v", method, path, query)
- }
- req, err := http.NewRequest(method, u.String(), body)
- if err != nil {
- return nil, err
- }
- resp, err := c.Do(req)
- if resp.StatusCode != http.StatusOK {
- errObj := new(Error)
- err = json.NewDecoder(resp.Body).Decode(errObj)
- if err != nil {
- return nil, err
- }
- return nil, fmt.Errorf("code=%s message=%s error=%s", errObj.Code, errObj.Message, errObj.Error)
- }
- return resp.Body, err
+func (a *Api) GetUpdates() chan UpdateInterface {
+ return a.updates
}
-// endregion
+func (a *Api) GetHandler(updates chan interface{}) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ defer func() {
+ if err := r.Body.Close(); err != nil {
+ log.Println(err)
+ }
+ }()
+ b, _ := ioutil.ReadAll(r.Body)
+ updates <- a.bytesToProperUpdate(b)
+ }
+}