0.1.8 Release
This commit is contained in:
parent
d13acd7da9
commit
94c96cef29
14 changed files with 1192 additions and 997 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -24,3 +24,5 @@ _testmain.go
|
|||
*.prof
|
||||
|
||||
.idea
|
||||
|
||||
tmp
|
73
README.md
73
README.md
|
@ -1,22 +1,75 @@
|
|||
# TamTam Go
|
||||
|
||||
Простая реализация клиента к TamTam Bot API на Go.
|
||||
[![Sourcegraph](https://sourcegraph.com/github.com/neonxp/tamtam/-/badge.svg?style=flat-square)](https://sourcegraph.com/github.com/neonxp/tamtam?badge)
|
||||
[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/neonxp/tamtam)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/neonxp/tamtam?style=flat-square)](https://goreportcard.com/report/github.com/neonxp/tamtam)
|
||||
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/neonxp/tamtam/master/LICENSE)
|
||||
|
||||
На данном этапе представляет собой 1 в 1 реализованные методы API + HTTPHandler для веб-хуков + сгенерированные модели.
|
||||
Простая реализация клиента к TamTam Bot API на Go. Поддерживается получение обновление как с помощью вебхуков, так и лонгполлингом.
|
||||
|
||||
Поддерживаемая версия API - 0.1.8
|
||||
|
||||
## Документация
|
||||
В общем случае, методы повторяют такие из [официальной документации](https://dev.tamtam.chat/)
|
||||
Так же добавлены хелпер для создания клавиатуры (`api.Messages.NewKeyboardBuilder()`) и для загрузки вложений (`api.Uploads.UploadMedia(uploadType UploadType, filename string)`). Пример создания клавиатуры см. ниже в примере.
|
||||
|
||||
Её пока нет :)
|
||||
|
||||
Но http://godoc.org/github.com/neonxp/tamtam/ может вам помочь.
|
||||
|
||||
## Статус
|
||||
|
||||
API пока крайне неставбильное и обязательно будет меняться в будущем. Учитывайте это!
|
||||
Остальное описано тут http://godoc.org/github.com/neonxp/tamtam/ и в примерах из директории [examples](https://github.com/neonxp/tamtam/tree/master/examples)
|
||||
|
||||
## Пример
|
||||
|
||||
Простые примеры использования находятся в директории `examples`.
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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)
|
||||
go api.UpdatesLoop(context.Background()) // Запуск цикла получения обновлений
|
||||
for upd := range api.GetUpdates() { // Чтение из канала с обновлениями
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Автор
|
||||
|
||||
|
|
572
api.go
572
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
|
||||
Bots *bots
|
||||
Chats *chats
|
||||
Messages *messages
|
||||
Subscriptions *subscriptions
|
||||
Uploads *uploads
|
||||
client *client
|
||||
updates chan UpdateInterface
|
||||
timeout int
|
||||
pause int
|
||||
logging bool
|
||||
}
|
||||
|
||||
// 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",
|
||||
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,
|
||||
logging: false,
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
jb, _ := ioutil.ReadAll(body)
|
||||
if a.logging {
|
||||
log.Printf("Received: %s", string(jb))
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
jb, _ := ioutil.ReadAll(body)
|
||||
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)
|
||||
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 nil, err
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
48
bots.go
Normal file
48
bots.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Package tamtam implements TamTam Bot API
|
||||
// Copyright (c) 2019 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
package tamtam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type bots struct {
|
||||
client *client
|
||||
}
|
||||
|
||||
func newBots(client *client) *bots {
|
||||
return &bots{client: client}
|
||||
}
|
||||
|
||||
func (a *bots) GetBot() (*BotInfo, error) {
|
||||
result := new(BotInfo)
|
||||
values := url.Values{}
|
||||
body, err := a.client.request(http.MethodGet, "me", values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *bots) PatchBot(patch *BotPatch) (*BotInfo, error) {
|
||||
result := new(BotInfo)
|
||||
values := url.Values{}
|
||||
body, err := a.client.request(http.MethodPatch, "me", values, patch)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
168
chats.go
Normal file
168
chats.go
Normal file
|
@ -0,0 +1,168 @@
|
|||
// Package tamtam implements TamTam Bot API
|
||||
// Copyright (c) 2019 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
package tamtam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type chats struct {
|
||||
client *client
|
||||
}
|
||||
|
||||
func newChats(client *client) *chats {
|
||||
return &chats{client: client}
|
||||
}
|
||||
|
||||
func (a *chats) GetChats(count, marker int) (*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.client.request(http.MethodGet, "chats", values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *chats) GetChat(chatID int) (*Chat, error) {
|
||||
result := new(Chat)
|
||||
values := url.Values{}
|
||||
body, err := a.client.request(http.MethodGet, fmt.Sprintf("chats/%d", chatID), values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *chats) GetChatMembership(chatID int) (*ChatMember, error) {
|
||||
result := new(ChatMember)
|
||||
values := url.Values{}
|
||||
body, err := a.client.request(http.MethodGet, fmt.Sprintf("chats/%d/members/me", chatID), values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *chats) GetChatMembers(chatID, count, marker int) (*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.client.request(http.MethodGet, fmt.Sprintf("chats/%d/members", chatID), values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *chats) LeaveChat(chatID int) (*SimpleQueryResult, error) {
|
||||
result := new(SimpleQueryResult)
|
||||
values := url.Values{}
|
||||
body, err := a.client.request(http.MethodDelete, fmt.Sprintf("chats/%d/members/me", chatID), values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *chats) EditChat(chatID int, update *ChatPatch) (*Chat, error) {
|
||||
result := new(Chat)
|
||||
values := url.Values{}
|
||||
body, err := a.client.request(http.MethodPatch, fmt.Sprintf("chats/%d", chatID), values, update)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *chats) AddMember(chatID int, users UserIdsList) (*SimpleQueryResult, error) {
|
||||
result := new(SimpleQueryResult)
|
||||
values := url.Values{}
|
||||
body, err := a.client.request(http.MethodPost, fmt.Sprintf("chats/%d/members", chatID), values, users)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *chats) RemoveMember(chatID int, userID int) (*SimpleQueryResult, error) {
|
||||
result := new(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)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *chats) SendAction(chatID int, action SenderAction) (*SimpleQueryResult, error) {
|
||||
result := new(SimpleQueryResult)
|
||||
values := url.Values{}
|
||||
body, err := a.client.request(http.MethodPost, fmt.Sprintf("chats/%d/actions", chatID), values, ActionRequestBody{Action: action})
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
56
client.go
Normal file
56
client.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Package tamtam implements TamTam Bot API
|
||||
// Copyright (c) 2019 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
package tamtam
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
key string
|
||||
version string
|
||||
url *url.URL
|
||||
}
|
||||
|
||||
func newClient(key string, version string, url *url.URL) *client {
|
||||
return &client{key: key, version: version, url: url}
|
||||
}
|
||||
|
||||
func (cl *client) request(method, path string, query url.Values, body interface{}) (io.ReadCloser, error) {
|
||||
j, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cl.requestReader(method, path, query, bytes.NewReader(j))
|
||||
}
|
||||
|
||||
func (cl *client) requestReader(method, path string, query url.Values, body io.Reader) (io.ReadCloser, error) {
|
||||
c := http.DefaultClient
|
||||
u := *cl.url
|
||||
u.Path = path
|
||||
query.Set("access_token", cl.key)
|
||||
query.Set("v", cl.version)
|
||||
u.RawQuery = query.Encode()
|
||||
req, err := http.NewRequest(method, u.String(), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
}
|
53
examples/example.go
Normal file
53
examples/example.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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)
|
||||
go api.UpdatesLoop(context.Background()) // Запуск цикла получения обновлений
|
||||
for upd := range api.GetUpdates() { // Чтение из канала с обновлениями
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,10 +8,11 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/neonxp/tamtam"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/neonxp/tamtam"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -19,28 +20,18 @@ func main() {
|
|||
api := tamtam.New(os.Getenv("TOKEN"))
|
||||
|
||||
// Some methods demo:
|
||||
info, err := api.GetMe()
|
||||
info, err := api.Bots.GetBot()
|
||||
log.Printf("Get me: %#v %#v", info, err)
|
||||
chats, err := api.GetChats(0, 0)
|
||||
log.Printf("Get chats: %#v %#v", chats, err)
|
||||
chat, err := api.GetChat(chats.Chats[0].ChatId)
|
||||
log.Printf("Get chat: %#v %#v", chat, err)
|
||||
subs, _ := api.GetSubscriptions()
|
||||
for _, s := range subs.Subscriptions {
|
||||
_, _ = api.Unsubscribe(s.Url)
|
||||
}
|
||||
ch := make(chan interface{}, 1) // Channel with updates from TamTam
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case upd := <-ch:
|
||||
case upd := <-api.GetUpdates():
|
||||
log.Printf("Received: %#v", upd)
|
||||
switch upd := upd.(type) {
|
||||
case tamtam.UpdateMessageCreated:
|
||||
res, err := api.SendMessage(0, upd.Message.Sender.UserId, &tamtam.NewMessageBody{
|
||||
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)
|
||||
|
@ -53,7 +44,6 @@ func main() {
|
|||
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
exit := make(chan os.Signal)
|
||||
signal.Notify(exit, os.Kill, os.Interrupt)
|
||||
|
@ -65,7 +55,7 @@ func main() {
|
|||
}
|
||||
}()
|
||||
|
||||
if err := api.GetUpdatesLoop(ctx, ch); err != nil {
|
||||
if err := api.UpdatesLoop(ctx); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
// +build ignore
|
||||
|
||||
/**
|
||||
* Webhook example
|
||||
*/
|
||||
|
@ -5,30 +7,27 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/neonxp/tamtam"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/neonxp/tamtam"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Initialisation
|
||||
api := tamtam.New(os.Getenv("TOKEN"))
|
||||
host := os.Getenv("HOST")
|
||||
|
||||
// Some methods demo:
|
||||
info, err := api.GetMe()
|
||||
info, err := api.Bots.GetBot()
|
||||
log.Printf("Get me: %#v %#v", info, err)
|
||||
chats, err := api.GetChats(0, 0)
|
||||
log.Printf("Get chats: %#v %#v", chats, err)
|
||||
chat, err := api.GetChat(chats.Chats[0].ChatId)
|
||||
log.Printf("Get chat: %#v %#v", chat, err)
|
||||
msgs, err := api.GetMessages(chats.Chats[0].ChatId, nil, 0, 0, 0)
|
||||
log.Printf("Get messages: %#v %#v", msgs, err)
|
||||
subs, _ := api.GetSubscriptions()
|
||||
|
||||
subs, _ := api.Subscriptions.GetSubscriptions()
|
||||
for _, s := range subs.Subscriptions {
|
||||
_, _ = api.Unsubscribe(s.Url)
|
||||
_, _ = api.Subscriptions.Unsubscribe(s.Url)
|
||||
}
|
||||
subscriptionResp, err := api.Subscribe("https://576df2ec.ngrok.io/webhook", []string{})
|
||||
subscriptionResp, err := api.Subscriptions.Subscribe(host+"/webhook", []string{})
|
||||
log.Printf("Subscription: %#v %#v", subscriptionResp, err)
|
||||
|
||||
ch := make(chan interface{}) // Channel with updates from TamTam
|
||||
|
@ -39,8 +38,8 @@ func main() {
|
|||
upd := <-ch
|
||||
log.Printf("Received: %#v", upd)
|
||||
switch upd := upd.(type) {
|
||||
case tamtam.UpdateMessageCreated:
|
||||
res, err := api.SendMessage(0, upd.Message.Sender.UserId, &tamtam.NewMessageBody{
|
||||
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)
|
|
@ -1,15 +1,11 @@
|
|||
// Package tamtam implements TamTam Bot API
|
||||
// Copyright (c) 2019 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
package tamtam
|
||||
|
||||
type KeyboardBuilder struct {
|
||||
rows []*KeyboardRow
|
||||
}
|
||||
|
||||
func NewKeyboardBuilder() *KeyboardBuilder {
|
||||
return &KeyboardBuilder{
|
||||
rows: make([]*KeyboardRow, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (k *KeyboardBuilder) AddRow() *KeyboardRow {
|
||||
kr := &KeyboardRow{}
|
||||
k.rows = append(k.rows, kr)
|
||||
|
@ -17,7 +13,7 @@ func (k *KeyboardBuilder) AddRow() *KeyboardRow {
|
|||
}
|
||||
|
||||
func (k *KeyboardBuilder) Build() Keyboard {
|
||||
buttons := make([][]interface{}, 0, len(k.rows))
|
||||
buttons := make([][]ButtonInterface, 0, len(k.rows))
|
||||
for _, r := range k.rows {
|
||||
buttons = append(buttons, r.Build())
|
||||
}
|
||||
|
@ -25,19 +21,20 @@ func (k *KeyboardBuilder) Build() Keyboard {
|
|||
}
|
||||
|
||||
type KeyboardRow struct {
|
||||
cols []interface{}
|
||||
cols []ButtonInterface
|
||||
}
|
||||
|
||||
func (k *KeyboardRow) Build() []interface{} {
|
||||
func (k *KeyboardRow) Build() []ButtonInterface {
|
||||
return k.cols
|
||||
}
|
||||
|
||||
func (k *KeyboardRow) AddLink(text string, intent Intent, url string) *KeyboardRow {
|
||||
b := LinkButton{
|
||||
Text: text,
|
||||
Url: url,
|
||||
Intent: intent,
|
||||
Button: Button{
|
||||
Text: text,
|
||||
Type: LINK,
|
||||
},
|
||||
}
|
||||
k.cols = append(k.cols, b)
|
||||
return k
|
||||
|
@ -45,31 +42,35 @@ func (k *KeyboardRow) AddLink(text string, intent Intent, url string) *KeyboardR
|
|||
|
||||
func (k *KeyboardRow) AddCallback(text string, intent Intent, payload string) *KeyboardRow {
|
||||
b := CallbackButton{
|
||||
Text: text,
|
||||
Payload: payload,
|
||||
Intent: intent,
|
||||
Button: Button{
|
||||
Text: text,
|
||||
Type: CALLBACK,
|
||||
},
|
||||
}
|
||||
k.cols = append(k.cols, b)
|
||||
return k
|
||||
}
|
||||
|
||||
func (k *KeyboardRow) AddContact(text string, intent Intent, url string) *KeyboardRow {
|
||||
func (k *KeyboardRow) AddContact(text string) *KeyboardRow {
|
||||
b := RequestContactButton{
|
||||
Button: Button{
|
||||
Text: text,
|
||||
Intent: intent,
|
||||
Type: CONTACT,
|
||||
},
|
||||
}
|
||||
k.cols = append(k.cols, b)
|
||||
return k
|
||||
}
|
||||
|
||||
func (k *KeyboardRow) AddGeolocation(text string, intent Intent, quick bool) *KeyboardRow {
|
||||
func (k *KeyboardRow) AddGeolocation(text string, quick bool) *KeyboardRow {
|
||||
b := RequestGeoLocationButton{
|
||||
Text: text,
|
||||
Quick: quick,
|
||||
Intent: intent,
|
||||
Button: Button{
|
||||
Text: text,
|
||||
Type: GEOLOCATION,
|
||||
},
|
||||
}
|
||||
k.cols = append(k.cols, b)
|
||||
return k
|
126
messages.go
Normal file
126
messages.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Package tamtam implements TamTam Bot API
|
||||
// Copyright (c) 2019 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
package tamtam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type messages struct {
|
||||
client *client
|
||||
}
|
||||
|
||||
func newMessages(client *client) *messages {
|
||||
return &messages{client: client}
|
||||
}
|
||||
|
||||
func (a *messages) GetMessages(chatID int, messageIDs []string, from int, to int, count int) (*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.client.request(http.MethodGet, "messages", values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *messages) DeleteMessage(messageID int) (*SimpleQueryResult, error) {
|
||||
result := new(SimpleQueryResult)
|
||||
values := url.Values{}
|
||||
values.Set("message_id", strconv.Itoa(int(messageID)))
|
||||
body, err := a.client.request(http.MethodDelete, "messages", values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *messages) AnswerOnCallback(callbackID int, callback *CallbackAnswer) (*SimpleQueryResult, error) {
|
||||
result := new(SimpleQueryResult)
|
||||
values := url.Values{}
|
||||
values.Set("callback_id", strconv.Itoa(int(callbackID)))
|
||||
body, err := a.client.request(http.MethodPost, "answers", values, callback)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *messages) NewKeyboardBuilder() *KeyboardBuilder {
|
||||
return &KeyboardBuilder{
|
||||
rows: make([]*KeyboardRow, 0),
|
||||
}
|
||||
}
|
68
subscriptions.go
Normal file
68
subscriptions.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Package tamtam implements TamTam Bot API
|
||||
// Copyright (c) 2019 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
package tamtam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type subscriptions struct {
|
||||
client *client
|
||||
}
|
||||
|
||||
func newSubscriptions(client *client) *subscriptions {
|
||||
return &subscriptions{client: client}
|
||||
}
|
||||
func (a *subscriptions) GetSubscriptions() (*GetSubscriptionsResult, error) {
|
||||
result := new(GetSubscriptionsResult)
|
||||
values := url.Values{}
|
||||
body, err := a.client.request(http.MethodGet, "subscriptions", values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *subscriptions) Subscribe(subscribeURL string, updateTypes []string) (*SimpleQueryResult, error) {
|
||||
subscription := &SubscriptionRequestBody{
|
||||
Url: subscribeURL,
|
||||
UpdateTypes: updateTypes,
|
||||
Version: a.client.version,
|
||||
}
|
||||
result := new(SimpleQueryResult)
|
||||
values := url.Values{}
|
||||
body, err := a.client.request(http.MethodPost, "subscriptions", values, subscription)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *subscriptions) Unsubscribe(subscriptionURL string) (*SimpleQueryResult, error) {
|
||||
result := new(SimpleQueryResult)
|
||||
values := url.Values{}
|
||||
values.Set("url", subscriptionURL)
|
||||
body, err := a.client.request(http.MethodDelete, "subscriptions", values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
84
uploads.go
Normal file
84
uploads.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Package tamtam implements TamTam Bot API
|
||||
// Copyright (c) 2019 Alexander Kiryukhin <a.kiryukhin@mail.ru>
|
||||
package tamtam
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
type uploads struct {
|
||||
client *client
|
||||
}
|
||||
|
||||
func newUploads(client *client) *uploads {
|
||||
return &uploads{client: client}
|
||||
}
|
||||
|
||||
func (a *uploads) GetUploadURL(uploadType UploadType) (*UploadEndpoint, error) {
|
||||
result := new(UploadEndpoint)
|
||||
values := url.Values{}
|
||||
values.Set("type", string(uploadType))
|
||||
body, err := a.client.request(http.MethodPost, "uploads", values, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer func() {
|
||||
if err := body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
return result, json.NewDecoder(body).Decode(result)
|
||||
}
|
||||
|
||||
func (a *uploads) UploadMedia(uploadType UploadType, filename string) (*UploadedInfo, 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
|
||||
}
|
||||
|
||||
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 func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
result := new(UploadedInfo)
|
||||
return result, json.NewDecoder(resp.Body).Decode(result)
|
||||
}
|
Loading…
Reference in a new issue