diff options
author | Alexander Kiryukhin <a.kiryukhin@mail.ru> | 2019-08-09 02:02:05 +0300 |
---|---|---|
committer | Alexander Kiryukhin <a.kiryukhin@mail.ru> | 2019-08-09 02:02:05 +0300 |
commit | 94c96cef29cd83bc60b2eabb8bb89c9fd8c7059b (patch) | |
tree | 676b3e2b6ffb171e102931e5fac01f7a4621c20a /api.go | |
parent | d13acd7da9bd1c08de33b91120a479df88d21db2 (diff) |
0.1.8 Release
Diffstat (limited to 'api.go')
-rw-r--r-- | api.go | 582 |
1 files changed, 117 insertions, 465 deletions
@@ -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) + } +} |