├── .gitignore ├── logo_bot.png ├── logo_msg.png ├── example ├── voice.aac ├── example.png └── main.go ├── voice.go ├── file.go ├── Makefile ├── options.go ├── go.mod ├── .github └── workflows │ └── go.yml ├── CONTRIBUTING.md ├── LICENSE ├── button.go ├── golangci.yml ├── updates.go ├── README.md ├── go.sum ├── file_easyjson.go ├── keyboard.go ├── chat_easyjson.go ├── chat.go ├── button_easyjson.go ├── types.go ├── message.go ├── api_mock.go ├── client_test.go ├── keyboard_test.go ├── message_easyjson.go ├── bot.go ├── client.go └── types_easyjson.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | cover.out 4 | .DS_Store -------------------------------------------------------------------------------- /logo_bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mail-ru-im/bot-golang/HEAD/logo_bot.png -------------------------------------------------------------------------------- /logo_msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mail-ru-im/bot-golang/HEAD/logo_msg.png -------------------------------------------------------------------------------- /example/voice.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mail-ru-im/bot-golang/HEAD/example/voice.aac -------------------------------------------------------------------------------- /example/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mail-ru-im/bot-golang/HEAD/example/example.png -------------------------------------------------------------------------------- /voice.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | var ( 4 | voiceMessageSupportedExtensions = map[string]bool{ 5 | ".aac": true, 6 | ".ogg": true, 7 | ".m4a": true, 8 | } 9 | ) 10 | 11 | const ( 12 | voiceMessageLeadingRune = 'I' 13 | ) 14 | -------------------------------------------------------------------------------- /file.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | //go:generate easyjson -all file.go 4 | 5 | type File struct { 6 | // Id of the file 7 | ID string `json:"fileId"` 8 | 9 | // Type of the file 10 | Type string `json:"type"` 11 | 12 | // Size in bytes 13 | Size uint64 `json:"size"` 14 | 15 | // Name of file 16 | Name string `json:"filename"` 17 | 18 | // URL to the file 19 | URL string `json:"url"` 20 | } 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: generate test 2 | 3 | GOPATH := $(shell go env GOPATH) 4 | 5 | $(GOPATH)/bin/easyjson: 6 | go build -mod mod -o $(GOPATH)/bin/easyjson github.com/mailru/easyjson/easyjson 7 | 8 | $(GOPATH)/bin/golangci-lint: 9 | go build -mod mod -o $(GOPATH)/bin/golangci-lint github.com/golangci/golangci-lint/cmd/golangci-lint 10 | 11 | .PHONY: test 12 | test: 13 | go test -v --cover -coverprofile=cover.out ./... 14 | 15 | .PHONY: lint 16 | lint: $(GOPATH)/bin/golangci-lint 17 | $(GOPATH)/bin/golangci-lint run 18 | 19 | .PHONY: generate 20 | generate: $(GOPATH)/bin/easyjson 21 | go generate 22 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | import "net/http" 4 | 5 | type BotOption interface { 6 | Type() string 7 | Value() interface{} 8 | } 9 | 10 | type BotApiURL string 11 | 12 | func (o BotApiURL) Type() string { 13 | return "api_url" 14 | } 15 | 16 | func (o BotApiURL) Value() interface{} { 17 | return string(o) 18 | } 19 | 20 | type BotDebug bool 21 | 22 | func (o BotDebug) Type() string { 23 | return "debug" 24 | } 25 | 26 | func (o BotDebug) Value() interface{} { 27 | return bool(o) 28 | } 29 | 30 | type BotHTTPClient http.Client 31 | 32 | func (o BotHTTPClient) Type() string { 33 | return "http_client" 34 | } 35 | 36 | func (o BotHTTPClient) Value() interface{} { 37 | return http.Client(o) 38 | } 39 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mail-ru-im/bot-golang 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b 7 | github.com/mailru/easyjson v0.7.7 8 | github.com/sirupsen/logrus v1.9.3 9 | github.com/stretchr/testify v1.7.0 10 | ) 11 | 12 | require ( 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/josharian/intern v1.0.0 // indirect 15 | github.com/kr/text v0.2.0 // indirect 16 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 17 | github.com/pmezard/go-difflib v1.0.0 // indirect 18 | golang.org/x/sys v0.18.0 // indirect 19 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect 20 | gopkg.in/yaml.v3 v3.0.0 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Go 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Set up Go 21 | uses: actions/setup-go@v4 22 | with: 23 | go-version: '1.21' 24 | 25 | - name: Lint 26 | run: make lint 27 | 28 | - name: Test 29 | run: make test 30 | 31 | - name: Upload coverage to Codecov 32 | uses: codecov/codecov-action@v4 33 | env: 34 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 35 | files: cover.out -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | ## Pull request 3 | Если вы решили впервые стать контрибьютером и помочь развитию open-source проекта, этот пункт для вас. 4 | 1) Делается fork основного репозитория 5 | 2) git clone https://github.com/ваш-логин/bot-golang.git 6 | 3) Локальное изменение 7 | 4) Сделайте ребейз на remote master ветку 8 | 5) git push origin <ваш-логин> 9 | 6) В удаленном репозитории нажать _compare&pull request_ 10 | 11 | Также рекомендуем ознакомиться с подробной инструкцией для контрибьютеров - README.md 12 | 13 | ## Tests 14 | 1) Если добавляется новая функциональность, то покрывайте ее тестами 15 | 2) Следите за тем, чтобы тесты успешно выполнялись в PR перед мержем. 16 | 17 | ## Merge 18 | Ветка будет смержена в мастер, когда: 19 | 1) Все пайплайны пройдут успешно 20 | 2) Новая функциональность будет покрыта тестами 21 | 22 | После выполнения всех пунктов один из сотрудников проверит в ближайшее время PR и смержит его. 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ICQ LLC (Mail.Ru Group) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /button.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | //go:generate easyjson -all button.go 4 | 5 | // Button represents a button in inline keyboard 6 | // Make sure you have URL or CallbackData in your Button. 7 | type Button struct { 8 | // Button text 9 | Text string `json:"text"` 10 | 11 | // URL to be opened 12 | // You can't use it with CallbackData 13 | URL string `json:"url,omitempty"` 14 | 15 | // Data that identify the button 16 | // You can't use it with URL 17 | CallbackData string `json:"callbackData,omitempty"` 18 | 19 | // Style of a button 20 | Style ButtonStyle `json:"style,omitempty"` 21 | } 22 | 23 | // ButtonStyle represent a style of a Button 24 | type ButtonStyle string 25 | 26 | const ( 27 | ButtonPrimary ButtonStyle = "primary" 28 | ButtonAttention ButtonStyle = "attention" 29 | ) 30 | 31 | // WithStyle sets ButtonStyle for Button 32 | func (v Button) WithStyle(style ButtonStyle) Button { 33 | v.Style = style 34 | return v 35 | } 36 | 37 | // NewURLButton returns new button with URL field 38 | func NewURLButton(text string, url string) Button { 39 | return Button{ 40 | Text: text, 41 | URL: url, 42 | } 43 | } 44 | 45 | // NewCallbackButton returns new button with CallbackData field 46 | func NewCallbackButton(text string, callbackData string) Button { 47 | return Button{ 48 | Text: text, 49 | CallbackData: callbackData, 50 | } 51 | } 52 | 53 | // ButtonResponse represents a data that is returned when a button is clicked 54 | type ButtonResponse struct { 55 | client *Client 56 | 57 | // Id of the query 58 | QueryID string `json:"queryId"` 59 | 60 | // Text of the response message 61 | Text string `json:"text"` 62 | 63 | // Display alert? 64 | ShowAlert bool `json:"showAlert"` 65 | 66 | // URL to be opened 67 | URL string `json:"url"` 68 | 69 | // CallbackData of the query (id of the pressed button). 70 | CallbackData string `json:"callbackData"` 71 | } 72 | 73 | // Send method sends your response message. 74 | // Make sure you have QueryID in your ButtonResponse. 75 | func (cl *ButtonResponse) Send() error { 76 | return cl.client.SendAnswerCallbackQuery(cl) 77 | } 78 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "os" 8 | "time" 9 | 10 | botgolang "github.com/mail-ru-im/bot-golang" 11 | ) 12 | 13 | func main() { 14 | token := os.Getenv("TOKEN") 15 | 16 | bot, err := botgolang.NewBot(token, botgolang.BotDebug(true)) 17 | if err != nil { 18 | log.Fatalf("cannot connect to bot: %s", err) 19 | } 20 | 21 | log.Println(bot.Info) 22 | 23 | message := bot.NewTextMessage("d.dorofeev@corp.mail.ru", "Hi") 24 | if err = message.Send(); err != nil { 25 | log.Fatalf("failed to send message: %s", err) 26 | } 27 | 28 | file, err := os.Open("./example.png") 29 | if err != nil { 30 | log.Fatalf("cannot open file: %s", err) 31 | } 32 | 33 | fileMessage := bot.NewFileMessage("d.dorofeev@corp.mail.ru", file) 34 | if err := fileMessage.Send(); err != nil { 35 | log.Println(err) 36 | } 37 | 38 | if err = fileMessage.Delete(); err != nil { 39 | log.Fatalf("failed to delete message: %s", err) 40 | } 41 | 42 | if err = file.Close(); err != nil { 43 | log.Fatalf("failed to close file: %s", err) 44 | } 45 | 46 | file, err = os.Open("./voice.aac") 47 | if err != nil { 48 | log.Fatalf("cannot open file: %s", err) 49 | } 50 | defer file.Close() 51 | 52 | voiceMessage := bot.NewVoiceMessage("g.gabolaev@corp.mail.ru", file) 53 | if err := voiceMessage.Send(); err != nil { 54 | log.Println(err) 55 | } 56 | 57 | // Simple 30-seconds echo bot with buttons 58 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 59 | defer cancel() 60 | updates := bot.GetUpdatesChannel(ctx) 61 | for update := range updates { 62 | fmt.Println(update.Type, update.Payload) 63 | switch update.Type { 64 | case botgolang.NEW_MESSAGE: 65 | message := update.Payload.Message() 66 | 67 | helloBtn := botgolang.NewCallbackButton("Hello", "echo") 68 | goBtn := botgolang.NewURLButton("go", "https://golang.org/") 69 | 70 | keyboard := botgolang.NewKeyboard() 71 | keyboard.AddRow(helloBtn, goBtn) 72 | 73 | message.AttachInlineKeyboard(keyboard) 74 | 75 | if err := message.Send(); err != nil { 76 | log.Printf("failed to send message: %s", err) 77 | } 78 | case botgolang.EDITED_MESSAGE: 79 | message := update.Payload.Message() 80 | if err := message.Reply("do not edit!"); err != nil { 81 | log.Printf("failed to reply to message: %s", err) 82 | } 83 | case botgolang.CALLBACK_QUERY: 84 | data := update.Payload.CallbackQuery() 85 | switch data.CallbackData { 86 | case "echo": 87 | response := bot.NewButtonResponse(data.QueryID, "", "Hello World!", false) 88 | if err := response.Send(); err != nil { 89 | log.Printf("failed to reply on button click: %s", err) 90 | } 91 | } 92 | } 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /golangci.yml: -------------------------------------------------------------------------------- 1 | # This file contains all available configuration options 2 | # with their default values. 3 | 4 | # options for analysis running 5 | run: 6 | # default concurrency is a available CPU number 7 | #concurrency: 4 8 | 9 | # timeout for analysis, e.g. 30s, 5m, default is 1m 10 | deadline: 30m 11 | 12 | # include test files or not, default is true 13 | tests: false 14 | 15 | # which dirs to skip: they won't be analyzed; 16 | # can use regexp here: generated.*, regexp is applied on full path; 17 | # default value is empty list, but next dirs are always skipped independently 18 | # from this option's value: 19 | # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ 20 | skip-dirs: 21 | 22 | # which files to skip: they will be analyzed, but issues from them 23 | # won't be reported. Default value is empty list, but there is 24 | # no need to include all autogenerated files, we confidently recognize 25 | # autogenerated files. If it's not please let us know. 26 | skip-files: 27 | - "_easyjson.go" 28 | - ".pb.go" 29 | 30 | # all available settings of specific linters 31 | linters-settings: 32 | errcheck: 33 | # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; 34 | # default is false: such cases aren't reported by default. 35 | check-blank: true 36 | 37 | govet: 38 | # report about shadowed variables 39 | check-shadowing: true 40 | 41 | golint: 42 | # minimal confidence for issues, default is 0.8 43 | min-confidence: 0.3 44 | gocyclo: 45 | # minimal code complexity to report, 30 by default (but we recommend 10-20) 46 | min-complexity: 15 47 | dupl: 48 | # tokens count to trigger issue, 150 by default 49 | threshold: 200 50 | lll: 51 | # max line length, lines longer will be reported. Default is 120. 52 | # '\t' is counted as 1 character by default, and can be changed with the tab-width option 53 | line-length: 120 54 | nakedret: 55 | # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 56 | max-func-lines: 30 57 | 58 | linters: 59 | enable-all: true 60 | disable: 61 | - gochecknoglobals 62 | - gochecknoinits 63 | - bodyclose # check issue https://github.com/timakin/bodyclose/issues/16 64 | fast: false 65 | 66 | issues: 67 | exclude: 68 | - "should have comment" 69 | - "always receives" 70 | - "parameter .* is always" 71 | - "comment on exported .* should be of the form" 72 | - "Use of weak cryptographic primitive" 73 | 74 | exclude-rules: 75 | # Exclude lll issues for long lines with go:generate 76 | - linters: 77 | - lll 78 | source: "^//go:generate " 79 | 80 | output: 81 | format: tab 82 | -------------------------------------------------------------------------------- /updates.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | dura "github.com/hako/durafmt" 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | const ( 13 | sleepTime = time.Second * 3 14 | ) 15 | 16 | var ( 17 | sleepTimeStr = dura.Parse(sleepTime) 18 | ) 19 | 20 | type Updater struct { 21 | logger *logrus.Logger 22 | client *Client 23 | lastEventID int 24 | PollTime int 25 | } 26 | 27 | // NewMessageFromPart returns new message based on part message 28 | func (u *Updater) NewMessageFromPayload(message EventPayload) *Message { 29 | return &Message{ 30 | client: u.client, 31 | ID: message.MsgID, 32 | Chat: Chat{ID: message.From.User.ID, Title: message.From.FirstName}, 33 | Text: message.Text, 34 | Timestamp: message.Timestamp, 35 | } 36 | } 37 | 38 | func (u *Updater) RunUpdatesCheck(ctx context.Context, ch chan<- Event) { 39 | _, err := u.GetLastEventsWithContext(ctx, 0) 40 | if err != nil { 41 | u.logger.WithFields(logrus.Fields{ 42 | "err": err, 43 | }).Debug("cannot make initial request to events") 44 | } 45 | 46 | for { 47 | select { 48 | case <-ctx.Done(): 49 | close(ch) 50 | return 51 | default: 52 | events, err := u.GetLastEventsWithContext(ctx, u.PollTime) 53 | if err != nil { 54 | u.logger.WithFields(logrus.Fields{ 55 | "err": err, 56 | "retry interval": sleepTimeStr, 57 | }).Errorf("Failed to get updates, retrying in %s ...", sleepTimeStr) 58 | time.Sleep(sleepTime) 59 | 60 | continue 61 | } 62 | 63 | for _, event := range events { 64 | event.client = u.client 65 | event.Payload.client = u.client 66 | 67 | ch <- *event 68 | } 69 | } 70 | } 71 | } 72 | 73 | func (u *Updater) GetLastEvents(pollTime int) ([]*Event, error) { 74 | return u.GetLastEventsWithContext(context.Background(), pollTime) 75 | } 76 | 77 | func (u *Updater) GetLastEventsWithContext(ctx context.Context, pollTime int) ([]*Event, error) { 78 | events, err := u.client.GetEventsWithContext(ctx, u.lastEventID, pollTime) 79 | if err != nil { 80 | u.logger.WithFields(logrus.Fields{ 81 | "err": err, 82 | "events": events, 83 | }).Debug("events getting error") 84 | return events, fmt.Errorf("cannot get events: %s", err) 85 | } 86 | 87 | count := len(events) 88 | if count > 0 { 89 | u.lastEventID = events[count-1].EventID 90 | } 91 | 92 | return events, nil 93 | } 94 | 95 | func NewUpdater(client *Client, pollTime int, logger *logrus.Logger) *Updater { 96 | if pollTime == 0 { 97 | pollTime = 60 98 | } 99 | 100 | return &Updater{ 101 | client: client, 102 | lastEventID: 0, 103 | PollTime: pollTime, 104 | logger: logger, 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # VK Teams Bot API for Golang 4 | [![Go](https://github.com/mail-ru-im/bot-golang/actions/workflows/go.yml/badge.svg)](https://github.com/mail-ru-im/bot-golang/actions/workflows/go.yml) 5 | [![codecov](https://codecov.io/github/mail-ru-im/bot-golang/graph/badge.svg?token=0HX8DY24SR)](https://codecov.io/github/mail-ru-im/bot-golang) 6 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/mail-ru-im/bot-golang) 7 | ![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg) 8 | 9 | ### [ VK Teams API Specification](https://teams.vk.com/botapi/) 10 | 11 | ## Getting started 12 | 13 | * Create your own bot by sending the _/newbot_ command to _Metabot_ and follow the instructions. 14 | >Note: a bot can only reply after the user has added it to his contact list, or if the user was the first to start a dialogue. 15 | * You can configure the domain that hosts your VK Teams server. When instantiating the Bot class, add the address of your domain. 16 | * An example of how to use the framework can be seen in _example/main.go_ 17 | 18 | ## Install 19 | ```bash 20 | go get github.com/mail-ru-im/bot-golang 21 | ``` 22 | 23 | ## Usage 24 | 25 | Create your own bot by sending the /newbot command to _Metabot_ and follow the instructions. 26 | Note a bot can only reply after the user has added it to his contacts list, or if the user was the first to start a dialogue. 27 | 28 | ### Create your bot 29 | 30 | ```go 31 | package main 32 | 33 | import "github.com/mail-ru-im/bot-golang" 34 | 35 | func main() { 36 | bot, err := botgolang.NewBot(BOT_TOKEN) 37 | if err != nil { 38 | log.Println("wrong token") 39 | } 40 | 41 | message := bot.NewTextMessage("some@mail.com", "text") 42 | message.Send() 43 | } 44 | ``` 45 | 46 | ### Send and edit messages 47 | 48 | You can create, edit and reply to messages like a piece of cake. 49 | 50 | ```go 51 | message := bot.NewTextMessage("some@mail.com", "text") 52 | message.Send() 53 | 54 | message.Text = "new text" 55 | 56 | message.Edit() 57 | message.Reply("I changed my text") 58 | ``` 59 | 60 | ### Subscribe events 61 | 62 | Get all updates from the channel. Use context for cancellation. 63 | 64 | ```go 65 | ctx, finish := context.WithCancel(context.Background()) 66 | updates := bot.GetUpdatesChannel(ctx) 67 | for update := range updates { 68 | // your logic here 69 | } 70 | ``` 71 | 72 | ### Passing options 73 | 74 | You don't need this. 75 | But if you do, you can override bot's API URL: 76 | 77 | ```go 78 | bot := botgolang.NewBot(BOT_TOKEN, botgolang.BotApiURL("https://vkteams.com/bot/v1")) 79 | ``` 80 | And debug all api requests and responses: 81 | 82 | ```go 83 | bot := botgolang.NewBot(BOT_TOKEN, botgolang.BotDebug(true)) 84 | ``` 85 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4= 6 | github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0= 7 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 8 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 9 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 10 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 11 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 12 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 13 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 14 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 15 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 16 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 17 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 18 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 19 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 20 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 21 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 22 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 23 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 24 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 25 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 26 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 27 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 28 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 29 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 30 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 31 | gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= 32 | gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 33 | -------------------------------------------------------------------------------- /file_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package botgolang 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson8ceb9162DecodeGithubComMailRuImBotGolang(in *jlexer.Lexer, out *File) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeFieldName(false) 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "fileId": 40 | out.ID = string(in.String()) 41 | case "type": 42 | out.Type = string(in.String()) 43 | case "size": 44 | out.Size = uint64(in.Uint64()) 45 | case "filename": 46 | out.Name = string(in.String()) 47 | case "url": 48 | out.URL = string(in.String()) 49 | default: 50 | in.SkipRecursive() 51 | } 52 | in.WantComma() 53 | } 54 | in.Delim('}') 55 | if isTopLevel { 56 | in.Consumed() 57 | } 58 | } 59 | func easyjson8ceb9162EncodeGithubComMailRuImBotGolang(out *jwriter.Writer, in File) { 60 | out.RawByte('{') 61 | first := true 62 | _ = first 63 | { 64 | const prefix string = ",\"fileId\":" 65 | out.RawString(prefix[1:]) 66 | out.String(string(in.ID)) 67 | } 68 | { 69 | const prefix string = ",\"type\":" 70 | out.RawString(prefix) 71 | out.String(string(in.Type)) 72 | } 73 | { 74 | const prefix string = ",\"size\":" 75 | out.RawString(prefix) 76 | out.Uint64(uint64(in.Size)) 77 | } 78 | { 79 | const prefix string = ",\"filename\":" 80 | out.RawString(prefix) 81 | out.String(string(in.Name)) 82 | } 83 | { 84 | const prefix string = ",\"url\":" 85 | out.RawString(prefix) 86 | out.String(string(in.URL)) 87 | } 88 | out.RawByte('}') 89 | } 90 | 91 | // MarshalJSON supports json.Marshaler interface 92 | func (v File) MarshalJSON() ([]byte, error) { 93 | w := jwriter.Writer{} 94 | easyjson8ceb9162EncodeGithubComMailRuImBotGolang(&w, v) 95 | return w.Buffer.BuildBytes(), w.Error 96 | } 97 | 98 | // MarshalEasyJSON supports easyjson.Marshaler interface 99 | func (v File) MarshalEasyJSON(w *jwriter.Writer) { 100 | easyjson8ceb9162EncodeGithubComMailRuImBotGolang(w, v) 101 | } 102 | 103 | // UnmarshalJSON supports json.Unmarshaler interface 104 | func (v *File) UnmarshalJSON(data []byte) error { 105 | r := jlexer.Lexer{Data: data} 106 | easyjson8ceb9162DecodeGithubComMailRuImBotGolang(&r, v) 107 | return r.Error() 108 | } 109 | 110 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 111 | func (v *File) UnmarshalEasyJSON(l *jlexer.Lexer) { 112 | easyjson8ceb9162DecodeGithubComMailRuImBotGolang(l, v) 113 | } 114 | -------------------------------------------------------------------------------- /keyboard.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Keyboard represents an inline keyboard markup 8 | // Call the NewKeyboard() func to get a keyboard instance 9 | type Keyboard struct { 10 | Rows [][]Button 11 | } 12 | 13 | // NewKeyboard returns a new keyboard instance 14 | func NewKeyboard() Keyboard { 15 | return Keyboard{ 16 | Rows: make([][]Button, 0), 17 | } 18 | } 19 | 20 | // AddRows adds a row to the keyboard 21 | func (k *Keyboard) AddRow(row ...Button) { 22 | k.Rows = append(k.Rows, row) 23 | } 24 | 25 | // AddButton adds a button to the end of the row 26 | func (k *Keyboard) AddButton(rowIndex int, button Button) error { 27 | if ok := k.checkRow(rowIndex); !ok { 28 | return fmt.Errorf("no such row: %d", rowIndex) 29 | } 30 | 31 | k.Rows[rowIndex] = append(k.Rows[rowIndex], button) 32 | return nil 33 | } 34 | 35 | // DeleteRow removes the row from the keyboard 36 | func (k *Keyboard) DeleteRow(index int) error { 37 | if ok := k.checkRow(index); !ok { 38 | return fmt.Errorf("no such row: %d", index) 39 | } 40 | 41 | k.Rows = append(k.Rows[:index], k.Rows[index+1:]...) 42 | return nil 43 | } 44 | 45 | // DeleteButton removes the button from the row. 46 | // Note - at least one button should remain in a row, 47 | // if you want to delete all buttons, use the DeleteRow function 48 | func (k *Keyboard) DeleteButton(rowIndex, buttonIndex int) error { 49 | if ok := k.checkButton(rowIndex, buttonIndex); !ok { 50 | return fmt.Errorf("no button at index %d or row %d", buttonIndex, rowIndex) 51 | } 52 | 53 | if k.RowSize(rowIndex) < 2 { 54 | return fmt.Errorf("can't delete button: at least one should remain in a row") 55 | } 56 | 57 | row := &k.Rows[rowIndex] 58 | *row = append((*row)[:buttonIndex], (*row)[buttonIndex+1:]...) 59 | return nil 60 | } 61 | 62 | // ChangeButton changes the button to a new one at the specified position 63 | func (k *Keyboard) ChangeButton(rowIndex, buttonIndex int, newButton Button) error { 64 | if ok := k.checkButton(rowIndex, buttonIndex); !ok { 65 | return fmt.Errorf("no button at index %d or row %d", buttonIndex, rowIndex) 66 | } 67 | 68 | k.Rows[rowIndex][buttonIndex] = newButton 69 | return nil 70 | } 71 | 72 | // SwapRows swaps two rows in keyboard 73 | func (k *Keyboard) SwapRows(first, second int) error { 74 | if ok := k.checkRow(first); !ok { 75 | return fmt.Errorf("no such index (first): %d", first) 76 | } 77 | if ok := k.checkRow(second); !ok { 78 | return fmt.Errorf("no such index (second): %d", second) 79 | } 80 | 81 | k.Rows[first], k.Rows[second] = k.Rows[second], k.Rows[first] 82 | return nil 83 | } 84 | 85 | // RowsCount returns the number of rows 86 | func (k *Keyboard) RowsCount() int { 87 | return len(k.Rows) 88 | } 89 | 90 | // RowSize returns the number of buttons in a row. 91 | // If there is no such row, then returns -1 92 | func (k *Keyboard) RowSize(row int) int { 93 | if ok := k.checkRow(row); !ok { 94 | return -1 95 | } 96 | return len(k.Rows[row]) 97 | } 98 | 99 | // GetKeyboard returns an array of button rows 100 | func (k *Keyboard) GetKeyboard() [][]Button { 101 | return k.Rows 102 | } 103 | 104 | // checkRow checks that the index of row doesnt go beyond the bounds of the array 105 | func (k *Keyboard) checkRow(i int) bool { 106 | return i >= 0 && i < len(k.Rows) 107 | } 108 | 109 | // checkButton checks that the button and row indexes doesnt go beyond the bounds of the array 110 | func (k *Keyboard) checkButton(row, button int) bool { 111 | return k.checkRow(row) && button >= 0 && button < len(k.Rows[row]) 112 | } 113 | -------------------------------------------------------------------------------- /chat_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package botgolang 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson9b8f5552DecodeGithubComMailRuImBotGolang(in *jlexer.Lexer, out *Chat) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeFieldName(false) 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "chatId": 40 | out.ID = string(in.String()) 41 | case "type": 42 | out.Type = string(in.String()) 43 | case "firstName": 44 | out.FirstName = string(in.String()) 45 | case "lastName": 46 | out.LastName = string(in.String()) 47 | case "nick": 48 | out.Nick = string(in.String()) 49 | case "about": 50 | out.About = string(in.String()) 51 | case "rules": 52 | out.Rules = string(in.String()) 53 | case "title": 54 | out.Title = string(in.String()) 55 | case "isBot": 56 | out.IsBot = bool(in.Bool()) 57 | case "public": 58 | out.Public = bool(in.Bool()) 59 | case "joinModeration": 60 | out.JoinModeration = bool(in.Bool()) 61 | case "inviteLink": 62 | out.InviteLink = string(in.String()) 63 | default: 64 | in.SkipRecursive() 65 | } 66 | in.WantComma() 67 | } 68 | in.Delim('}') 69 | if isTopLevel { 70 | in.Consumed() 71 | } 72 | } 73 | func easyjson9b8f5552EncodeGithubComMailRuImBotGolang(out *jwriter.Writer, in Chat) { 74 | out.RawByte('{') 75 | first := true 76 | _ = first 77 | { 78 | const prefix string = ",\"chatId\":" 79 | out.RawString(prefix[1:]) 80 | out.String(string(in.ID)) 81 | } 82 | { 83 | const prefix string = ",\"type\":" 84 | out.RawString(prefix) 85 | out.String(string(in.Type)) 86 | } 87 | { 88 | const prefix string = ",\"firstName\":" 89 | out.RawString(prefix) 90 | out.String(string(in.FirstName)) 91 | } 92 | { 93 | const prefix string = ",\"lastName\":" 94 | out.RawString(prefix) 95 | out.String(string(in.LastName)) 96 | } 97 | { 98 | const prefix string = ",\"nick\":" 99 | out.RawString(prefix) 100 | out.String(string(in.Nick)) 101 | } 102 | { 103 | const prefix string = ",\"about\":" 104 | out.RawString(prefix) 105 | out.String(string(in.About)) 106 | } 107 | { 108 | const prefix string = ",\"rules\":" 109 | out.RawString(prefix) 110 | out.String(string(in.Rules)) 111 | } 112 | { 113 | const prefix string = ",\"title\":" 114 | out.RawString(prefix) 115 | out.String(string(in.Title)) 116 | } 117 | { 118 | const prefix string = ",\"isBot\":" 119 | out.RawString(prefix) 120 | out.Bool(bool(in.IsBot)) 121 | } 122 | { 123 | const prefix string = ",\"public\":" 124 | out.RawString(prefix) 125 | out.Bool(bool(in.Public)) 126 | } 127 | { 128 | const prefix string = ",\"joinModeration\":" 129 | out.RawString(prefix) 130 | out.Bool(bool(in.JoinModeration)) 131 | } 132 | { 133 | const prefix string = ",\"inviteLink\":" 134 | out.RawString(prefix) 135 | out.String(string(in.InviteLink)) 136 | } 137 | out.RawByte('}') 138 | } 139 | 140 | // MarshalJSON supports json.Marshaler interface 141 | func (v Chat) MarshalJSON() ([]byte, error) { 142 | w := jwriter.Writer{} 143 | easyjson9b8f5552EncodeGithubComMailRuImBotGolang(&w, v) 144 | return w.Buffer.BuildBytes(), w.Error 145 | } 146 | 147 | // MarshalEasyJSON supports easyjson.Marshaler interface 148 | func (v Chat) MarshalEasyJSON(w *jwriter.Writer) { 149 | easyjson9b8f5552EncodeGithubComMailRuImBotGolang(w, v) 150 | } 151 | 152 | // UnmarshalJSON supports json.Unmarshaler interface 153 | func (v *Chat) UnmarshalJSON(data []byte) error { 154 | r := jlexer.Lexer{Data: data} 155 | easyjson9b8f5552DecodeGithubComMailRuImBotGolang(&r, v) 156 | return r.Error() 157 | } 158 | 159 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 160 | func (v *Chat) UnmarshalEasyJSON(l *jlexer.Lexer) { 161 | easyjson9b8f5552DecodeGithubComMailRuImBotGolang(l, v) 162 | } 163 | -------------------------------------------------------------------------------- /chat.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | //go:generate easyjson -all chat.go 4 | 5 | type ChatAction = string 6 | 7 | const ( 8 | TypingAction ChatAction = "typing" 9 | LookingAction ChatAction = "looking" 10 | ) 11 | 12 | type ChatType = string 13 | 14 | const ( 15 | Private ChatType = "private" 16 | Group ChatType = "group" 17 | Channel ChatType = "channel" 18 | ) 19 | 20 | type Chat struct { 21 | client *Client 22 | // Id of the chat 23 | ID string `json:"chatId"` 24 | 25 | // Type of the chat: channel, group or private 26 | Type ChatType `json:"type"` 27 | 28 | // First name of the user 29 | FirstName string `json:"firstName"` 30 | 31 | // Last name of the user 32 | LastName string `json:"lastName"` 33 | 34 | // Nick of the user 35 | Nick string `json:"nick"` 36 | 37 | // User about or group/channel description 38 | About string `json:"about"` 39 | 40 | // Rules of the group/channel 41 | Rules string `json:"rules"` 42 | 43 | // Title of the chat 44 | Title string `json:"title"` 45 | 46 | // Flag that indicates that requested chat is the bot 47 | IsBot bool `json:"isBot"` 48 | 49 | // Is this chat public? 50 | Public bool `json:"public"` 51 | 52 | // Is this chat has join moderation? 53 | JoinModeration bool `json:"joinModeration"` 54 | 55 | // You can send this link to all your friends 56 | InviteLink string `json:"inviteLink"` 57 | } 58 | 59 | func (c *Chat) resolveID() string { 60 | switch c.Type { 61 | case Private: 62 | return c.Nick 63 | default: 64 | return c.ID 65 | } 66 | } 67 | 68 | // Send bot actions to the chat 69 | // 70 | // You can call this method every time you change the current actions, 71 | // or every 10 seconds if the actions have not changed. After sending a 72 | // request without active action, you should not re-notify of their absence. 73 | func (c *Chat) SendActions(actions ...ChatAction) error { 74 | return c.client.SendChatActions(c.resolveID(), actions...) 75 | } 76 | 77 | // Get chat administrators list 78 | func (c *Chat) GetAdmins() ([]ChatMember, error) { 79 | return c.client.GetChatAdmins(c.ID) 80 | } 81 | 82 | // Get chat members list 83 | func (c *Chat) GetMembers() ([]ChatMember, error) { 84 | return c.client.GetChatMembers(c.ID) 85 | } 86 | 87 | // Get chat blocked users list 88 | func (c *Chat) GetBlockedUsers() ([]User, error) { 89 | return c.client.GetChatBlockedUsers(c.ID) 90 | } 91 | 92 | // Get chat join pending users list 93 | func (c *Chat) GetPendingUsers() ([]User, error) { 94 | return c.client.GetChatPendingUsers(c.ID) 95 | } 96 | 97 | // DeleteMembers removes members from chat 98 | func (c *Chat) DeleteMembers(members []string) error { 99 | return c.client.DeleteChatMembers(c.ID, members) 100 | } 101 | 102 | // AddMembers adds members to chat 103 | func (c *Chat) AddMembers(members []string) error { 104 | return c.client.AddChatMembers(c.ID, members) 105 | } 106 | 107 | // Block user and remove him from chat. 108 | // If deleteLastMessages is true, the messages written recently will be deleted 109 | func (c *Chat) BlockUser(userID string, deleteLastMessages bool) error { 110 | return c.client.BlockChatUser(c.ID, userID, deleteLastMessages) 111 | } 112 | 113 | // Unblock user in chat (but not add him back) 114 | func (c *Chat) UnblockUser(userID string) error { 115 | return c.client.UnblockChatUser(c.ID, userID) 116 | } 117 | 118 | // ResolveJoinRequest resolve specific user chat join request 119 | func (c *Chat) ResolveJoinRequest(userID string, accept bool) error { 120 | return c.client.ResolveChatPending(c.ID, userID, accept, false) 121 | } 122 | 123 | // ResolveAllJoinRequest resolve all chat join requests 124 | func (c *Chat) ResolveAllJoinRequests(accept bool) error { 125 | return c.client.ResolveChatPending(c.ID, "", accept, true) 126 | } 127 | 128 | // SetTitle changes chat title 129 | func (c *Chat) SetTitle(title string) error { 130 | return c.client.SetChatTitle(c.ID, title) 131 | } 132 | 133 | // SetAbout changes chat about 134 | func (c *Chat) SetAbout(about string) error { 135 | return c.client.SetChatAbout(c.ID, about) 136 | } 137 | 138 | // SetRules changes chat rules 139 | func (c *Chat) SetRules(rules string) error { 140 | return c.client.SetChatRules(c.ID, rules) 141 | } 142 | 143 | // AddThread adds a new thread to the specified chat and returns the thread ID 144 | func (c *Chat) AddThread(msgID string) (*Thread, error) { 145 | return c.client.AddThread(c.ID, msgID) 146 | } 147 | 148 | // AutosubscribeToThreads toggles thread auto-subscription for the chat 149 | func (c *Chat) AutosubscribeToThreads(enable, withExisting bool) error { 150 | return c.client.AutosubscribeToThreads(c.ID, enable, withExisting) 151 | } 152 | -------------------------------------------------------------------------------- /button_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package botgolang 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjsonF248ab8DecodeGithubComMailRuImBotGolang(in *jlexer.Lexer, out *ButtonResponse) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeFieldName(false) 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "queryId": 40 | out.QueryID = string(in.String()) 41 | case "text": 42 | out.Text = string(in.String()) 43 | case "showAlert": 44 | out.ShowAlert = bool(in.Bool()) 45 | case "url": 46 | out.URL = string(in.String()) 47 | case "callbackData": 48 | out.CallbackData = string(in.String()) 49 | default: 50 | in.SkipRecursive() 51 | } 52 | in.WantComma() 53 | } 54 | in.Delim('}') 55 | if isTopLevel { 56 | in.Consumed() 57 | } 58 | } 59 | func easyjsonF248ab8EncodeGithubComMailRuImBotGolang(out *jwriter.Writer, in ButtonResponse) { 60 | out.RawByte('{') 61 | first := true 62 | _ = first 63 | { 64 | const prefix string = ",\"queryId\":" 65 | out.RawString(prefix[1:]) 66 | out.String(string(in.QueryID)) 67 | } 68 | { 69 | const prefix string = ",\"text\":" 70 | out.RawString(prefix) 71 | out.String(string(in.Text)) 72 | } 73 | { 74 | const prefix string = ",\"showAlert\":" 75 | out.RawString(prefix) 76 | out.Bool(bool(in.ShowAlert)) 77 | } 78 | { 79 | const prefix string = ",\"url\":" 80 | out.RawString(prefix) 81 | out.String(string(in.URL)) 82 | } 83 | { 84 | const prefix string = ",\"callbackData\":" 85 | out.RawString(prefix) 86 | out.String(string(in.CallbackData)) 87 | } 88 | out.RawByte('}') 89 | } 90 | 91 | // MarshalJSON supports json.Marshaler interface 92 | func (v ButtonResponse) MarshalJSON() ([]byte, error) { 93 | w := jwriter.Writer{} 94 | easyjsonF248ab8EncodeGithubComMailRuImBotGolang(&w, v) 95 | return w.Buffer.BuildBytes(), w.Error 96 | } 97 | 98 | // MarshalEasyJSON supports easyjson.Marshaler interface 99 | func (v ButtonResponse) MarshalEasyJSON(w *jwriter.Writer) { 100 | easyjsonF248ab8EncodeGithubComMailRuImBotGolang(w, v) 101 | } 102 | 103 | // UnmarshalJSON supports json.Unmarshaler interface 104 | func (v *ButtonResponse) UnmarshalJSON(data []byte) error { 105 | r := jlexer.Lexer{Data: data} 106 | easyjsonF248ab8DecodeGithubComMailRuImBotGolang(&r, v) 107 | return r.Error() 108 | } 109 | 110 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 111 | func (v *ButtonResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { 112 | easyjsonF248ab8DecodeGithubComMailRuImBotGolang(l, v) 113 | } 114 | func easyjsonF248ab8DecodeGithubComMailRuImBotGolang1(in *jlexer.Lexer, out *Button) { 115 | isTopLevel := in.IsStart() 116 | if in.IsNull() { 117 | if isTopLevel { 118 | in.Consumed() 119 | } 120 | in.Skip() 121 | return 122 | } 123 | in.Delim('{') 124 | for !in.IsDelim('}') { 125 | key := in.UnsafeFieldName(false) 126 | in.WantColon() 127 | if in.IsNull() { 128 | in.Skip() 129 | in.WantComma() 130 | continue 131 | } 132 | switch key { 133 | case "text": 134 | out.Text = string(in.String()) 135 | case "url": 136 | out.URL = string(in.String()) 137 | case "callbackData": 138 | out.CallbackData = string(in.String()) 139 | case "style": 140 | out.Style = ButtonStyle(in.String()) 141 | default: 142 | in.SkipRecursive() 143 | } 144 | in.WantComma() 145 | } 146 | in.Delim('}') 147 | if isTopLevel { 148 | in.Consumed() 149 | } 150 | } 151 | func easyjsonF248ab8EncodeGithubComMailRuImBotGolang1(out *jwriter.Writer, in Button) { 152 | out.RawByte('{') 153 | first := true 154 | _ = first 155 | { 156 | const prefix string = ",\"text\":" 157 | out.RawString(prefix[1:]) 158 | out.String(string(in.Text)) 159 | } 160 | if in.URL != "" { 161 | const prefix string = ",\"url\":" 162 | out.RawString(prefix) 163 | out.String(string(in.URL)) 164 | } 165 | if in.CallbackData != "" { 166 | const prefix string = ",\"callbackData\":" 167 | out.RawString(prefix) 168 | out.String(string(in.CallbackData)) 169 | } 170 | if in.Style != "" { 171 | const prefix string = ",\"style\":" 172 | out.RawString(prefix) 173 | out.String(string(in.Style)) 174 | } 175 | out.RawByte('}') 176 | } 177 | 178 | // MarshalJSON supports json.Marshaler interface 179 | func (v Button) MarshalJSON() ([]byte, error) { 180 | w := jwriter.Writer{} 181 | easyjsonF248ab8EncodeGithubComMailRuImBotGolang1(&w, v) 182 | return w.Buffer.BuildBytes(), w.Error 183 | } 184 | 185 | // MarshalEasyJSON supports easyjson.Marshaler interface 186 | func (v Button) MarshalEasyJSON(w *jwriter.Writer) { 187 | easyjsonF248ab8EncodeGithubComMailRuImBotGolang1(w, v) 188 | } 189 | 190 | // UnmarshalJSON supports json.Unmarshaler interface 191 | func (v *Button) UnmarshalJSON(data []byte) error { 192 | r := jlexer.Lexer{Data: data} 193 | easyjsonF248ab8DecodeGithubComMailRuImBotGolang1(&r, v) 194 | return r.Error() 195 | } 196 | 197 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 198 | func (v *Button) UnmarshalEasyJSON(l *jlexer.Lexer) { 199 | easyjsonF248ab8DecodeGithubComMailRuImBotGolang1(l, v) 200 | } 201 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | //go:generate easyjson -all types.go 4 | 5 | type EventType string 6 | 7 | type PartType string 8 | 9 | const ( 10 | NEW_MESSAGE EventType = "newMessage" 11 | EDITED_MESSAGE EventType = "editedMessage" 12 | DELETED_MESSAGE EventType = "deletedMessage" 13 | PINNED_MESSAGE EventType = "pinnedMessage" 14 | UNPINNED_MESSAGE EventType = "unpinnedMessage" 15 | NEW_CHAT_MEMBERS EventType = "newChatMembers" 16 | LEFT_CHAT_MEMBERS EventType = "leftChatMembers" 17 | CALLBACK_QUERY EventType = "callbackQuery" 18 | 19 | STICKER PartType = "sticker" 20 | MENTION PartType = "mention" 21 | VOICE PartType = "voice" 22 | FILE PartType = "file" 23 | FORWARD PartType = "forward" 24 | REPLY PartType = "reply" 25 | ) 26 | 27 | type Response struct { 28 | OK bool `json:"ok"` 29 | Description string `json:"description,omitempty"` 30 | } 31 | 32 | type Thread struct { 33 | ThreadID string `json:"threadId"` 34 | } 35 | 36 | type UserState struct { 37 | Lastseen int `json:"lastseen"` 38 | } 39 | 40 | type Subscriber struct { 41 | SN string `json:"sn"` 42 | UserState UserState `json:"userState"` 43 | } 44 | 45 | type ThreadSubscribers struct { 46 | Cursor string `json:"cursor"` 47 | Subscribers []Subscriber `json:"subscribers"` 48 | } 49 | 50 | type Photo struct { 51 | URL string `json:"url"` 52 | } 53 | 54 | type BotInfo struct { 55 | User 56 | 57 | // Nickname of the bot 58 | Nick string `json:"nick"` 59 | 60 | // Name of the bot 61 | FirstName string `json:"firstName"` 62 | 63 | // Information about the box 64 | About string `json:"about"` 65 | 66 | // A slice of avatars 67 | Photo []Photo `json:"photo"` 68 | } 69 | 70 | type eventsResponse struct { 71 | OK bool `json:"ok"` 72 | Events []*Event `json:"events"` 73 | } 74 | 75 | type User struct { 76 | ID string `json:"userId"` 77 | } 78 | 79 | type ChatMember struct { 80 | User 81 | Creator bool `json:"creator"` 82 | Admin bool `json:"admin"` 83 | } 84 | 85 | type UsersListResponse struct { 86 | List []User `json:"users"` 87 | } 88 | 89 | type MembersListResponse struct { 90 | // TODO: cursor 91 | List []ChatMember `json:"members"` 92 | } 93 | 94 | type AdminsListResponse struct { 95 | List []ChatMember `json:"admins"` 96 | } 97 | 98 | type Contact struct { 99 | User 100 | FirstName string `json:"firstName"` 101 | LastName string `json:"lastName"` 102 | } 103 | 104 | type BaseEventPayload struct { 105 | // Id of the message. 106 | // Presented in newMessage, editedMessage, deletedMessage, pinnedMessage, unpinnedMessage events. 107 | MsgID string `json:"msgId"` 108 | 109 | // Chat info. 110 | // Presented in all events. 111 | Chat Chat `json:"chat"` 112 | 113 | // Author of the message 114 | // Presented in newMessage and editedMessage events. 115 | From Contact `json:"from"` 116 | 117 | // Text of the message. 118 | // Presented in newMessage, editedMessage and pinnedMessage events. 119 | Text string `json:"text"` 120 | 121 | // Timestamp of the event. 122 | Timestamp int `json:"timestamp"` 123 | 124 | ParentMessage *ParentMessage `json:"parent_topic"` 125 | } 126 | 127 | type EventPayload struct { 128 | client *Client 129 | BaseEventPayload 130 | 131 | // Parts of the message. 132 | // Presented only in newMessage event. 133 | Parts []Part `json:"parts"` 134 | 135 | // Id of the query. 136 | // Presented only in callbackQuery event. 137 | QueryID string `json:"queryId"` 138 | 139 | // Callback message of the query (parent message for button). 140 | // Presented only in callbackQuery event. 141 | CallbackMsg BaseEventPayload `json:"message"` 142 | 143 | // CallbackData of the query (id of button). 144 | // Presented only in callbackQuery event. 145 | CallbackData string `json:"callbackData"` 146 | 147 | LeftMembers []Contact `json:"leftMembers"` 148 | 149 | NewMembers []Contact `json:"newMembers"` 150 | 151 | AddedBy Contact `json:"addedBy"` 152 | 153 | RemovedBy Contact `json:"removedBy"` 154 | } 155 | 156 | func (ep *EventPayload) Message() *Message { 157 | return message(ep.client, ep.BaseEventPayload) 158 | } 159 | 160 | func (ep *EventPayload) CallbackMessage() *Message { 161 | return message(ep.client, ep.CallbackMsg) 162 | } 163 | 164 | func message(client *Client, msg BaseEventPayload) *Message { 165 | msg.Chat.client = client 166 | return &Message{ 167 | client: client, 168 | ID: msg.MsgID, 169 | Text: msg.Text, 170 | Chat: msg.Chat, 171 | Timestamp: msg.Timestamp, 172 | ParentMessage: msg.ParentMessage, 173 | } 174 | } 175 | 176 | type PartMessage struct { 177 | From Contact `json:"from"` 178 | MsgID string `json:"msgId"` 179 | Text string `json:"text"` 180 | Timestamp int `json:"timestamp"` 181 | } 182 | 183 | type PartPayload struct { 184 | FirstName string `json:"firstName"` 185 | LastName string `json:"lastName"` 186 | UserID string `json:"userId"` 187 | FileID string `json:"fileId"` 188 | Caption string `json:"caption"` 189 | Type string `json:"type"` 190 | PartMessage PartMessage `json:"message"` 191 | Message PartMessage `json:"-"` 192 | } 193 | 194 | type Event struct { 195 | client *Client 196 | 197 | // Id of the event 198 | EventID int `json:"eventId"` 199 | 200 | // Type of the event: newMessage, editedMessage, deletedMessage, pinnedMessage, unpinnedMessage, newChatMembers 201 | Type EventType `json:"type"` 202 | 203 | // Payload of the event 204 | Payload EventPayload `json:"payload"` 205 | } 206 | 207 | type Part struct { 208 | // Type of the part 209 | Type PartType `json:"type"` 210 | 211 | // Payload of the part 212 | Payload PartPayload `json:"payload"` 213 | } 214 | 215 | func (ep *EventPayload) CallbackQuery() *ButtonResponse { 216 | return &ButtonResponse{ 217 | client: ep.client, 218 | QueryID: ep.QueryID, 219 | CallbackData: ep.CallbackData, 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /message.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | //go:generate easyjson -all message.go 10 | 11 | type MessageContentType uint8 12 | 13 | const ( 14 | Unknown MessageContentType = iota 15 | Text 16 | OtherFile 17 | Voice 18 | Deeplink 19 | ) 20 | 21 | // Message represents a text message 22 | type Message struct { 23 | client *Client 24 | ContentType MessageContentType 25 | 26 | // Id of the message (for editing) 27 | ID string `json:"msgId"` 28 | 29 | // File contains file attachment of the message 30 | File *os.File `json:"-"` 31 | 32 | // Id of file to send 33 | FileID string `json:"fileId"` 34 | 35 | // Text of the message or caption for file 36 | Text string `json:"text"` 37 | 38 | // Chat where to send the message 39 | Chat Chat `json:"chat"` 40 | 41 | // Id of replied message 42 | // You can't use it with ForwardMsgID or ForwardChatID 43 | ReplyMsgID string `json:"replyMsgId"` 44 | 45 | // Id of forwarded message 46 | // You can't use it with ReplyMsgID 47 | ForwardMsgID string `json:"forwardMsgId"` 48 | 49 | // Id of a chat from which you forward the message 50 | // You can't use it with ReplyMsgID 51 | // You should use it with ForwardMsgID 52 | ForwardChatID string `json:"forwardChatId"` 53 | 54 | Timestamp int `json:"timestamp"` 55 | 56 | ParentMessage *ParentMessage `json:"parent_topic"` 57 | 58 | // The markup for the inline keyboard 59 | InlineKeyboard *Keyboard `json:"inlineKeyboardMarkup"` 60 | 61 | // The parse mode (HTML/MarkdownV2) 62 | ParseMode ParseMode `json:"parseMode"` 63 | 64 | // RequestID from library clients that is used in my-team logs 65 | RequestID string `json:"requestID"` 66 | 67 | // Use it only with content type Deeplink 68 | Deeplink string `json:"deeplink"` 69 | } 70 | 71 | func (m *Message) AttachNewFile(file *os.File) { 72 | m.File = file 73 | m.ContentType = OtherFile 74 | } 75 | 76 | func (m *Message) AttachExistingFile(fileID string) { 77 | m.FileID = fileID 78 | m.ContentType = OtherFile 79 | } 80 | 81 | func (m *Message) AttachNewVoice(file *os.File) { 82 | m.File = file 83 | m.ContentType = Voice 84 | } 85 | 86 | func (m *Message) AttachExistingVoice(fileID string) { 87 | m.FileID = fileID 88 | m.ContentType = Voice 89 | } 90 | 91 | type ParentMessage struct { 92 | ChatID string `json:"chatId"` 93 | MsgID int64 `json:"messageId"` 94 | Type string `json:"type"` 95 | } 96 | 97 | // ParseMode represent a type of text formatting 98 | type ParseMode string 99 | 100 | const ( 101 | ParseModeHTML ParseMode = "HTML" 102 | ParseModeMarkdownV2 ParseMode = "MarkdownV2" 103 | ) 104 | 105 | // AppendParseMode append a type of text formatting for current message 106 | func (m *Message) AppendParseMode(mode ParseMode) { 107 | m.ParseMode = mode 108 | } 109 | 110 | // AttachInlineKeyboard adds a keyboard to the message. 111 | // Note - at least one row should be in the keyboard 112 | // and there should be no empty rows 113 | func (m *Message) AttachInlineKeyboard(keyboard Keyboard) { 114 | m.InlineKeyboard = &keyboard 115 | } 116 | 117 | // Send method sends your message. 118 | // Make sure you have Text or FileID in your message. 119 | func (m *Message) Send() error { 120 | if m.client == nil { 121 | return fmt.Errorf("client is not inited, create message with constructor NewMessage, NewTextMessage, etc") 122 | } 123 | 124 | if m.Chat.ID == "" { 125 | return fmt.Errorf("message should have chat id") 126 | } 127 | 128 | switch m.ContentType { 129 | case Voice: 130 | if m.FileID != "" { 131 | return m.client.SendVoiceMessage(m) 132 | } 133 | 134 | if m.File != nil { 135 | return m.client.UploadVoice(m) 136 | } 137 | case OtherFile: 138 | if m.FileID != "" { 139 | return m.client.SendFileMessage(m) 140 | } 141 | 142 | if m.File != nil { 143 | return m.client.UploadFile(m) 144 | } 145 | case Text: 146 | return m.client.SendTextMessage(m) 147 | case Deeplink: 148 | return m.client.SendTextWithDeeplinkMessage(m) 149 | case Unknown: 150 | // need to autodetect 151 | if m.FileID != "" { 152 | // voice message's fileID always starts with 'I' 153 | if m.FileID[0] == voiceMessageLeadingRune { 154 | return m.client.SendVoiceMessage(m) 155 | } 156 | return m.client.SendFileMessage(m) 157 | } 158 | 159 | if m.File != nil { 160 | if voiceMessageSupportedExtensions[filepath.Ext(m.File.Name())] { 161 | return m.client.UploadVoice(m) 162 | } 163 | return m.client.UploadFile(m) 164 | } 165 | 166 | if m.Text != "" { 167 | return m.client.SendTextMessage(m) 168 | } 169 | } 170 | 171 | return fmt.Errorf("cannot send message or file without data") 172 | } 173 | 174 | // Edit method edits your message. 175 | // Make sure you have ID in your message. 176 | func (m *Message) Edit() error { 177 | if m.ID == "" { 178 | return fmt.Errorf("cannot edit message without id") 179 | } 180 | return m.client.EditMessage(m) 181 | } 182 | 183 | // Delete method deletes your message. 184 | // Make sure you have ID in your message. 185 | func (m *Message) Delete() error { 186 | if m.ID == "" { 187 | return fmt.Errorf("cannot delete message without id") 188 | } 189 | 190 | return m.client.DeleteMessage(m) 191 | } 192 | 193 | // Reply method replies to the message. 194 | // Make sure you have ID in the message. 195 | func (m *Message) Reply(text string) error { 196 | if m.ID == "" { 197 | return fmt.Errorf("cannot reply to message without id") 198 | } 199 | 200 | m.ReplyMsgID = m.ID 201 | m.Text = text 202 | 203 | return m.client.SendTextMessage(m) 204 | } 205 | 206 | // Forward method forwards your message to chat. 207 | // Make sure you have ID in your message. 208 | func (m *Message) Forward(chatID string) error { 209 | if m.ID == "" { 210 | return fmt.Errorf("cannot forward message without id") 211 | } 212 | 213 | m.ForwardChatID = m.Chat.ID 214 | m.ForwardMsgID = m.ID 215 | m.Chat.ID = chatID 216 | 217 | return m.client.SendTextMessage(m) 218 | } 219 | 220 | // Pin message in chat 221 | // Make sure you are admin in this chat 222 | func (m *Message) Pin() error { 223 | if m.ID == "" { 224 | return fmt.Errorf("cannot pin message without id") 225 | } 226 | 227 | return m.client.PinMessage(m) 228 | } 229 | 230 | // Unpin message in chat 231 | // Make sure you are admin in this chat 232 | func (m *Message) Unpin() error { 233 | if m.ID == "" { 234 | return fmt.Errorf("cannot unpin message without id") 235 | } 236 | 237 | return m.client.UnpinMessage(m) 238 | } 239 | -------------------------------------------------------------------------------- /api_mock.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/sirupsen/logrus" 8 | ) 9 | 10 | type MockHandler struct { 11 | http.Handler 12 | logger *logrus.Logger 13 | } 14 | 15 | func (h *MockHandler) SendMessage(w http.ResponseWriter) { 16 | encoder := json.NewEncoder(w) 17 | err := encoder.Encode(&Response{ 18 | OK: true, 19 | }) 20 | 21 | if err != nil { 22 | h.logger.WithFields(logrus.Fields{ 23 | "err": err, 24 | }).Error("cannot encode json") 25 | } 26 | } 27 | 28 | func (h *MockHandler) TokenError(w http.ResponseWriter) { 29 | encoder := json.NewEncoder(w) 30 | err := encoder.Encode(&Response{ 31 | OK: false, 32 | Description: "Missing required parameter 'token'", 33 | }) 34 | 35 | if err != nil { 36 | h.logger.WithFields(logrus.Fields{ 37 | "err": err, 38 | }).Error("cannot encode json") 39 | } 40 | } 41 | 42 | func (h *MockHandler) GetEvents(w http.ResponseWriter) { 43 | events := ` 44 | { 45 | "ok": true, 46 | "events": [ 47 | { 48 | "eventId": 1, 49 | "type": "newMessage", 50 | "payload": { 51 | "msgId": "57883346846815030", 52 | "chat": { 53 | "chatId": "681869378@chat.agent", 54 | "type": "channel", 55 | "title": "The best channel" 56 | }, 57 | "from": { 58 | "userId": "1234567890", 59 | "firstName": "Name", 60 | "lastName": "SurName" 61 | }, 62 | "timestamp": 1546290000, 63 | "text": "Hello!", 64 | "parts": [ 65 | { 66 | "type": "sticker", 67 | "payload": { 68 | "fileId": "2IWuJzaNWCJZxJWCvZhDYuJ5XDsr7hU" 69 | } 70 | }, 71 | { 72 | "type": "mention", 73 | "payload": { 74 | "userId": "1234567890", 75 | "firstName": "Name", 76 | "lastName": "SurName" 77 | } 78 | }, 79 | { 80 | "type": "voice", 81 | "payload": { 82 | "fileId": "IdjUEXuGdNhLKUfD5rvkE03IOax54cD" 83 | } 84 | }, 85 | { 86 | "type": "file", 87 | "payload": { 88 | "fileId": "ZhSnMuaOmF7FRez2jGWuQs5zGZwlLa0", 89 | "type": "image", 90 | "caption": "Last weekend trip" 91 | } 92 | }, 93 | { 94 | "type": "forward", 95 | "payload": { 96 | "message": { 97 | "msgId": "12354", 98 | "text": "test1" 99 | } 100 | } 101 | }, 102 | { 103 | "type": "reply", 104 | "payload": { 105 | "message": { 106 | "msgId": "12354", 107 | "text": "test" 108 | } 109 | } 110 | } 111 | ] 112 | } 113 | }, 114 | { 115 | "eventId": 2, 116 | "type": "editedMessage", 117 | "payload": { 118 | "msgId": "57883346846815030", 119 | "chat": { 120 | "chatId": "681869378@chat.agent", 121 | "type": "channel", 122 | "title": "The best channel" 123 | }, 124 | "from": { 125 | "userId": "1234567890", 126 | "firstName": "Name", 127 | "lastName": "SurName" 128 | }, 129 | "timestamp": 1546290000, 130 | "text": "Hello!", 131 | "editedTimestamp": 1546290099 132 | } 133 | }, 134 | { 135 | "eventId": 3, 136 | "type": "deletedMessage", 137 | "payload": { 138 | "msgId": "57883346846815030", 139 | "chat": { 140 | "chatId": "681869378@chat.agent", 141 | "type": "channel", 142 | "title": "The best channel" 143 | }, 144 | "timestamp": 1546290000 145 | } 146 | }, 147 | { 148 | "eventId": 4, 149 | "type": "pinnedMessage", 150 | "payload": { 151 | "chat": { 152 | "chatId": "681869378@chat.agent", 153 | "type": "group", 154 | "title": "The best group" 155 | }, 156 | "from": { 157 | "userId": "9876543210", 158 | "firstName": "Name", 159 | "lastName": "SurName" 160 | }, 161 | "msgId": "6720509406122810000", 162 | "text": "Some important information!", 163 | "timestamp": 1564740530 164 | } 165 | }, 166 | { 167 | "eventId": 5, 168 | "type": "unpinnedMessage", 169 | "payload": { 170 | "chat": { 171 | "chatId": "681869378@chat.agent", 172 | "type": "group", 173 | "title": "The best group" 174 | }, 175 | "msgId": "6720509406122810000", 176 | "timestamp": 1564740530 177 | } 178 | }, 179 | { 180 | "eventId": 6, 181 | "type": "newChatMembers", 182 | "payload": { 183 | "chat": { 184 | "chatId": "681869378@chat.agent", 185 | "type": "group", 186 | "title": "The best group" 187 | }, 188 | "newMembers": [ 189 | { 190 | "userId": "1234567890", 191 | "firstName": "Name", 192 | "lastName": "SurName" 193 | } 194 | ], 195 | "addedBy": { 196 | "userId": "9876543210", 197 | "firstName": "Name", 198 | "lastName": "SurName" 199 | } 200 | } 201 | }, 202 | { 203 | "eventId": 7, 204 | "type": "leftChatMembers", 205 | "payload": { 206 | "chat": { 207 | "chatId": "681869378@chat.agent", 208 | "type": "group", 209 | "title": "The best group" 210 | }, 211 | "leftMembers": [ 212 | { 213 | "userId": "1234567890", 214 | "firstName": "Name", 215 | "lastName": "SurName" 216 | } 217 | ], 218 | "removedBy": { 219 | "userId": "9876543210", 220 | "firstName": "Name", 221 | "lastName": "SurName" 222 | } 223 | } 224 | }, 225 | { 226 | "eventId": 8, 227 | "payload": { 228 | "callbackData": "echo", 229 | "from": { 230 | "firstName": "Name", 231 | "userId": "1234567890" 232 | }, 233 | "message": { 234 | "chat": { 235 | "chatId": "1234567890", 236 | "type": "private" 237 | }, 238 | "from": { 239 | "firstName": "bot_name", 240 | "nick": "bot_nick", 241 | "userId": "bot_id" 242 | }, 243 | "msgId": "6720509406122810000", 244 | "text": "Some important information!", 245 | "timestamp": 1564740530 246 | }, 247 | "queryId": "SVR:123456" 248 | }, 249 | "type": "callbackQuery" 250 | } 251 | ] 252 | } 253 | ` 254 | 255 | _, err := w.Write([]byte(events)) 256 | if err != nil { 257 | h.logger.Fatal("failed to write events") 258 | } 259 | } 260 | 261 | func (h *MockHandler) SelfGet(w http.ResponseWriter, r *http.Request) { 262 | 263 | encoder := json.NewEncoder(w) 264 | 265 | if r.FormValue("chatId") == "" { 266 | err := encoder.Encode(&Response{ 267 | OK: false, 268 | Description: "Missing required parameter 'chatId'", 269 | }) 270 | 271 | if err != nil { 272 | h.logger.WithFields(logrus.Fields{ 273 | "err": err, 274 | }).Error("cannot encode json") 275 | } 276 | } 277 | 278 | chats_getInfo := `{ 279 | "about": "about user", 280 | "firstName": "User", 281 | "language": "en", 282 | "lastName": "Userov", 283 | "photo": [ 284 | { 285 | "url": "https://rapi.myteaminternal/avatar/get?targetSn=test@test&size=1024" 286 | } 287 | ], 288 | "type": "private", 289 | "ok": true 290 | }` 291 | 292 | _, err := w.Write([]byte(chats_getInfo)) 293 | if err != nil { 294 | h.logger.Fatal("failed to write events") 295 | } 296 | } 297 | 298 | func (h *MockHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 299 | switch { 300 | case r.FormValue("token") == "": 301 | h.TokenError(w) 302 | return 303 | case r.URL.Path == "/messages/sendText": 304 | h.SendMessage(w) 305 | return 306 | case r.URL.Path == "/events/get": 307 | h.GetEvents(w) 308 | return 309 | case r.URL.Path == "/self/get": 310 | h.SelfGet(w, r) 311 | return 312 | default: 313 | encoder := json.NewEncoder(w) 314 | err := encoder.Encode(&Response{ 315 | OK: true, 316 | }) 317 | 318 | if err != nil { 319 | h.logger.WithFields(logrus.Fields{ 320 | "err": err, 321 | }).Error("cannot encode response") 322 | } 323 | } 324 | 325 | } 326 | -------------------------------------------------------------------------------- /client_test.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "net/url" 7 | "testing" 8 | 9 | "github.com/sirupsen/logrus" 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestClient_Do_OK(t *testing.T) { 15 | assert := assert.New(t) 16 | testServer := httptest.NewServer(&MockHandler{}) 17 | defer func() { testServer.Close() }() 18 | 19 | client := Client{ 20 | baseURL: testServer.URL, 21 | token: "test_token", 22 | client: http.DefaultClient, 23 | logger: &logrus.Logger{}, 24 | } 25 | 26 | bytes, err := client.Do("/", url.Values{}, nil) 27 | 28 | assert.NoError(err) 29 | assert.JSONEq(`{"ok":true}`, string(bytes)) 30 | } 31 | 32 | func TestClient_Do_Error(t *testing.T) { 33 | assert := assert.New(t) 34 | testServer := httptest.NewServer(&MockHandler{}) 35 | defer func() { testServer.Close() }() 36 | 37 | client := Client{ 38 | baseURL: testServer.URL, 39 | token: "", 40 | client: http.DefaultClient, 41 | logger: &logrus.Logger{}, 42 | } 43 | 44 | expected := `{"ok":false, "description":"Missing required parameter 'token'"}` 45 | 46 | bytes, err := client.Do("/", url.Values{}, nil) 47 | 48 | assert.EqualError(err, "error status from API: Missing required parameter 'token'") 49 | assert.JSONEq(expected, string(bytes)) 50 | } 51 | 52 | func TestClient_GetEvents_OK(t *testing.T) { 53 | assert := assert.New(t) 54 | require := require.New(t) 55 | testServer := httptest.NewServer(&MockHandler{}) 56 | defer func() { testServer.Close() }() 57 | 58 | expected := []*Event{ 59 | { 60 | EventID: 1, 61 | Type: NEW_MESSAGE, 62 | Payload: EventPayload{ 63 | BaseEventPayload: BaseEventPayload{ 64 | MsgID: "57883346846815030", 65 | Chat: Chat{ 66 | ID: "681869378@chat.agent", 67 | Type: "channel", 68 | Title: "The best channel", 69 | }, 70 | From: Contact{ 71 | User: User{"1234567890"}, 72 | FirstName: "Name", 73 | LastName: "SurName", 74 | }, 75 | Text: "Hello!", 76 | Timestamp: 1546290000, 77 | }, 78 | Parts: []Part{ 79 | { 80 | Type: STICKER, 81 | Payload: PartPayload{ 82 | FileID: "2IWuJzaNWCJZxJWCvZhDYuJ5XDsr7hU", 83 | }, 84 | }, 85 | { 86 | Type: MENTION, 87 | Payload: PartPayload{ 88 | FirstName: "Name", 89 | LastName: "SurName", 90 | UserID: "1234567890", 91 | }, 92 | }, 93 | { 94 | Type: VOICE, 95 | Payload: PartPayload{ 96 | FileID: "IdjUEXuGdNhLKUfD5rvkE03IOax54cD", 97 | }, 98 | }, 99 | { 100 | Type: FILE, 101 | Payload: PartPayload{ 102 | FileID: "ZhSnMuaOmF7FRez2jGWuQs5zGZwlLa0", 103 | Caption: "Last weekend trip", 104 | Type: "image", 105 | }, 106 | }, 107 | { 108 | Type: FORWARD, 109 | Payload: PartPayload{ 110 | PartMessage: PartMessage{ 111 | MsgID: "12354", 112 | Text: "test1", 113 | }, 114 | }, 115 | }, 116 | { 117 | Type: REPLY, 118 | Payload: PartPayload{ 119 | PartMessage: PartMessage{ 120 | MsgID: "12354", 121 | Text: "test", 122 | }, 123 | }, 124 | }, 125 | }, 126 | }, 127 | }, 128 | { 129 | EventID: 2, 130 | Type: EDITED_MESSAGE, 131 | Payload: EventPayload{ 132 | BaseEventPayload: BaseEventPayload{ 133 | MsgID: "57883346846815030", 134 | Chat: Chat{ 135 | ID: "681869378@chat.agent", 136 | Type: "channel", 137 | Title: "The best channel", 138 | }, 139 | From: Contact{ 140 | User: User{"1234567890"}, 141 | FirstName: "Name", 142 | LastName: "SurName", 143 | }, 144 | Text: "Hello!", 145 | Timestamp: 1546290000, 146 | }, 147 | }, 148 | }, 149 | { 150 | EventID: 3, 151 | Type: DELETED_MESSAGE, 152 | Payload: EventPayload{ 153 | BaseEventPayload: BaseEventPayload{ 154 | MsgID: "57883346846815030", 155 | Chat: Chat{ 156 | ID: "681869378@chat.agent", 157 | Type: "channel", 158 | Title: "The best channel", 159 | }, 160 | Timestamp: 1546290000, 161 | }, 162 | }, 163 | }, 164 | { 165 | EventID: 4, 166 | Type: PINNED_MESSAGE, 167 | Payload: EventPayload{ 168 | BaseEventPayload: BaseEventPayload{ 169 | MsgID: "6720509406122810000", 170 | Chat: Chat{ 171 | ID: "681869378@chat.agent", 172 | Type: "group", 173 | Title: "The best group", 174 | }, 175 | From: Contact{ 176 | User: User{"9876543210"}, 177 | FirstName: "Name", 178 | LastName: "SurName", 179 | }, 180 | Text: "Some important information!", 181 | Timestamp: 1564740530, 182 | }, 183 | }, 184 | }, 185 | { 186 | EventID: 5, 187 | Type: UNPINNED_MESSAGE, 188 | Payload: EventPayload{ 189 | BaseEventPayload: BaseEventPayload{ 190 | MsgID: "6720509406122810000", 191 | Chat: Chat{ 192 | ID: "681869378@chat.agent", 193 | Type: "group", 194 | Title: "The best group", 195 | }, 196 | Timestamp: 1564740530, 197 | }, 198 | }, 199 | }, 200 | { 201 | EventID: 6, 202 | Type: NEW_CHAT_MEMBERS, 203 | Payload: EventPayload{ 204 | BaseEventPayload: BaseEventPayload{ 205 | Chat: Chat{ 206 | ID: "681869378@chat.agent", 207 | Type: "group", 208 | Title: "The best group", 209 | }, 210 | }, 211 | NewMembers: []Contact{ 212 | { 213 | User: User{"1234567890"}, 214 | FirstName: "Name", 215 | LastName: "SurName", 216 | }, 217 | }, 218 | AddedBy: Contact{ 219 | User: User{"9876543210"}, 220 | FirstName: "Name", 221 | LastName: "SurName", 222 | }, 223 | }, 224 | }, 225 | { 226 | EventID: 7, 227 | Type: LEFT_CHAT_MEMBERS, 228 | Payload: EventPayload{ 229 | BaseEventPayload: BaseEventPayload{ 230 | Chat: Chat{ 231 | ID: "681869378@chat.agent", 232 | Type: "group", 233 | Title: "The best group", 234 | }, 235 | }, 236 | LeftMembers: []Contact{ 237 | { 238 | User: User{"1234567890"}, 239 | FirstName: "Name", 240 | LastName: "SurName", 241 | }, 242 | }, 243 | RemovedBy: Contact{ 244 | User: User{"9876543210"}, 245 | FirstName: "Name", 246 | LastName: "SurName", 247 | }, 248 | }, 249 | }, 250 | { 251 | EventID: 8, 252 | Type: CALLBACK_QUERY, 253 | Payload: EventPayload{ 254 | CallbackData: "echo", 255 | CallbackMsg: BaseEventPayload{ 256 | MsgID: "6720509406122810000", 257 | Chat: Chat{ 258 | ID: "1234567890", 259 | Type: "private", 260 | }, 261 | From: Contact{ 262 | User: User{"bot_id"}, 263 | FirstName: "bot_name", 264 | }, 265 | Text: "Some important information!", 266 | Timestamp: 1564740530, 267 | }, 268 | BaseEventPayload: BaseEventPayload{ 269 | From: Contact{ 270 | User: User{"1234567890"}, 271 | FirstName: "Name", 272 | }, 273 | }, 274 | QueryID: "SVR:123456", 275 | }, 276 | }, 277 | } 278 | 279 | client := Client{ 280 | baseURL: testServer.URL, 281 | token: "test_token", 282 | client: http.DefaultClient, 283 | logger: &logrus.Logger{}, 284 | } 285 | 286 | events, err := client.GetEvents(0, 0) 287 | 288 | require.NoError(err) 289 | assert.Equal(events, expected) 290 | } 291 | 292 | func TestClient_GetInfo_OK(t *testing.T) { 293 | require := require.New(t) 294 | assert := assert.New(t) 295 | testServer := httptest.NewServer(&MockHandler{}) 296 | defer func() { testServer.Close() }() 297 | 298 | client := Client{ 299 | baseURL: testServer.URL, 300 | token: "test_token", 301 | client: http.DefaultClient, 302 | logger: &logrus.Logger{}, 303 | } 304 | 305 | info, err := client.GetChatInfo("id_1234") 306 | require.NoError(err) 307 | assert.NotEmpty(info.ID) 308 | } 309 | 310 | func TestClient_GetInfo_Error(t *testing.T) { 311 | require := require.New(t) 312 | 313 | require.NoError(nil) 314 | } 315 | -------------------------------------------------------------------------------- /keyboard_test.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | btnArray1 = []Button{{Text: "test"}, {Text: "test2"}} 11 | btnArray2 = []Button{{Text: "tes123t"}, {Text: "test123"}, {Text: "test123"}, {Text: "test2231"}} 12 | btnArray3 = []Button{{Text: "ew"}} 13 | ) 14 | 15 | type fields struct { 16 | Rows [][]Button 17 | } 18 | 19 | func TestKeyboard_AddButton(t *testing.T) { 20 | type args struct { 21 | rowIndex int 22 | button Button 23 | } 24 | 25 | btn := NewURLButton("test", "mail.ru") 26 | btn2 := NewCallbackButton("test2", "asdfww34gsw35") 27 | btnRow := []Button{btn, btn2} 28 | 29 | newBtn := NewCallbackButton("newBtn", "sdtw234") 30 | 31 | expected := []Button{btn, btn2, newBtn} 32 | 33 | tests := []struct { 34 | name string 35 | fields fields 36 | args args 37 | exp fields 38 | wantErr bool 39 | }{ 40 | { 41 | name: "OK", 42 | fields: fields{[][]Button{btnRow}}, 43 | args: args{0, newBtn}, 44 | exp: fields{[][]Button{expected}}, 45 | wantErr: false, 46 | }, 47 | { 48 | name: "Error", 49 | fields: fields{[][]Button{btnRow}}, 50 | args: args{4, newBtn}, 51 | exp: fields{[][]Button{btnRow}}, 52 | wantErr: true, 53 | }, 54 | } 55 | for _, tt := range tests { 56 | t.Run(tt.name, func(t *testing.T) { 57 | k := &Keyboard{ 58 | Rows: tt.fields.Rows, 59 | } 60 | if err := k.AddButton(tt.args.rowIndex, tt.args.button); (err != nil) != tt.wantErr { 61 | t.Errorf("AddButton() error = %v, wantErr %v", err, tt.wantErr) 62 | } 63 | assert.Equal(t, reflect.DeepEqual(tt.exp.Rows, k.GetKeyboard()), true) 64 | }) 65 | } 66 | } 67 | 68 | func TestKeyboard_AddRow(t *testing.T) { 69 | type args struct { 70 | row []Button 71 | } 72 | btn := NewURLButton("test", "mail.ru") 73 | btn2 := NewCallbackButton("test2", "asdfww34gsw35") 74 | btnRow := []Button{btn} 75 | btnRow2 := []Button{btn2} 76 | 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | exp fields 82 | }{ 83 | { 84 | name: "OK_First", 85 | fields: fields{nil}, 86 | args: args{row: btnRow}, 87 | exp: fields{[][]Button{btnRow}}, 88 | }, 89 | { 90 | name: "OK_Add", 91 | fields: fields{[][]Button{btnRow}}, 92 | args: args{row: btnRow2}, 93 | exp: fields{[][]Button{btnRow, btnRow2}}, 94 | }, 95 | { 96 | name: "Nil", 97 | fields: fields{nil}, 98 | args: args{row: nil}, 99 | exp: fields{[][]Button{[]Button(nil)}}, 100 | }, 101 | } 102 | for _, tt := range tests { 103 | t.Run(tt.name, func(t *testing.T) { 104 | k := &Keyboard{ 105 | Rows: tt.fields.Rows, 106 | } 107 | k.AddRow(tt.args.row...) 108 | assert.Equal(t, tt.exp.Rows, k.GetKeyboard()) 109 | }) 110 | } 111 | } 112 | 113 | func TestKeyboard_ChangeButton(t *testing.T) { 114 | type args struct { 115 | rowIndex int 116 | buttonIndex int 117 | newButton Button 118 | } 119 | newBtn := Button{Text: "TestButton"} 120 | 121 | array1 := make([]Button, len(btnArray1)) 122 | copy(array1, btnArray1) 123 | 124 | expArray := make([]Button, len(array1)) 125 | expArray[0] = newBtn 126 | expArray[1] = btnArray1[1] 127 | 128 | tests := []struct { 129 | name string 130 | fields fields 131 | args args 132 | exp fields 133 | wantErr bool 134 | }{ 135 | { 136 | name: "OK", 137 | fields: fields{[][]Button{array1}}, 138 | args: args{0, 0, newBtn}, 139 | exp: fields{[][]Button{expArray}}, 140 | wantErr: false, 141 | }, 142 | { 143 | name: "Error", 144 | fields: fields{[][]Button{array1}}, 145 | args: args{0, 5, newBtn}, 146 | exp: fields{[][]Button{expArray}}, 147 | wantErr: true, 148 | }, 149 | } 150 | for _, tt := range tests { 151 | t.Run(tt.name, func(t *testing.T) { 152 | k := &Keyboard{ 153 | Rows: tt.fields.Rows, 154 | } 155 | if err := k.ChangeButton(tt.args.rowIndex, tt.args.buttonIndex, tt.args.newButton); (err != nil) != tt.wantErr { 156 | t.Errorf("ChangeButton() error = %v, wantErr %v", err, tt.wantErr) 157 | } 158 | assert.Equal(t, reflect.DeepEqual(tt.exp.Rows, k.GetKeyboard()), true) 159 | }) 160 | } 161 | } 162 | 163 | func TestKeyboard_DeleteButton(t *testing.T) { 164 | type args struct { 165 | rowIndex int 166 | buttonIndex int 167 | } 168 | 169 | array1 := [][]Button{btnArray1} 170 | exp1 := [][]Button{{btnArray1[1]}} 171 | 172 | tests := []struct { 173 | name string 174 | fields fields 175 | args args 176 | exp fields 177 | wantErr bool 178 | }{ 179 | { 180 | name: "OK", 181 | fields: fields{array1}, 182 | args: args{0, 0}, 183 | exp: fields{exp1}, 184 | wantErr: false, 185 | }, 186 | { 187 | name: "Error", 188 | fields: fields{array1}, 189 | args: args{2, 13}, 190 | exp: fields{exp1}, 191 | wantErr: true, 192 | }, 193 | } 194 | for _, tt := range tests { 195 | t.Run(tt.name, func(t *testing.T) { 196 | k := &Keyboard{ 197 | Rows: tt.fields.Rows, 198 | } 199 | if err := k.DeleteButton(tt.args.rowIndex, tt.args.buttonIndex); (err != nil) != tt.wantErr { 200 | t.Errorf("DeleteButton() error = %v, wantErr %v", err, tt.wantErr) 201 | } 202 | assert.Equal(t, tt.exp.Rows, k.GetKeyboard()) 203 | }) 204 | } 205 | } 206 | 207 | func TestKeyboard_DeleteRow(t *testing.T) { 208 | type args struct { 209 | index int 210 | } 211 | 212 | tests := []struct { 213 | name string 214 | fields fields 215 | args args 216 | exp fields 217 | wantErr bool 218 | }{ 219 | { 220 | name: "OK", 221 | fields: fields{[][]Button{btnArray1}}, 222 | args: args{0}, 223 | exp: fields{[][]Button{}}, 224 | wantErr: false, 225 | }, 226 | { 227 | name: "Error", 228 | fields: fields{[][]Button{btnArray1}}, 229 | args: args{1}, 230 | exp: fields{[][]Button{btnArray1}}, 231 | wantErr: true, 232 | }, 233 | } 234 | for _, tt := range tests { 235 | t.Run(tt.name, func(t *testing.T) { 236 | k := &Keyboard{ 237 | Rows: tt.fields.Rows, 238 | } 239 | if err := k.DeleteRow(tt.args.index); (err != nil) != tt.wantErr { 240 | t.Errorf("DeleteRow() error = %v, wantErr %v", err, tt.wantErr) 241 | } 242 | assert.Equal(t, tt.exp.Rows, k.GetKeyboard()) 243 | }) 244 | } 245 | } 246 | 247 | func TestKeyboard_RowSize(t *testing.T) { 248 | type args struct { 249 | row int 250 | } 251 | tests := []struct { 252 | name string 253 | fields fields 254 | args args 255 | want int 256 | }{ 257 | { 258 | name: "OK", 259 | fields: fields{[][]Button{btnArray2}}, 260 | args: args{0}, 261 | want: len(btnArray2), 262 | }, 263 | { 264 | name: "OK", 265 | fields: fields{[][]Button{btnArray2, btnArray3}}, 266 | args: args{1}, 267 | want: len(btnArray3), 268 | }, 269 | { 270 | name: "NO_ROW", 271 | fields: fields{[][]Button{btnArray2, btnArray3}}, 272 | args: args{4}, 273 | want: -1, 274 | }, 275 | } 276 | for _, tt := range tests { 277 | t.Run(tt.name, func(t *testing.T) { 278 | k := &Keyboard{ 279 | Rows: tt.fields.Rows, 280 | } 281 | if got := k.RowSize(tt.args.row); got != tt.want { 282 | t.Errorf("RowSize() = %v, want %v", got, tt.want) 283 | } 284 | }) 285 | } 286 | } 287 | 288 | func TestKeyboard_RowsCount(t *testing.T) { 289 | tests := []struct { 290 | name string 291 | fields fields 292 | want int 293 | }{ 294 | { 295 | name: "OK_3", 296 | fields: fields{[][]Button{btnArray1, btnArray2, btnArray3}}, 297 | want: 3, 298 | }, 299 | { 300 | name: "OK_2", 301 | fields: fields{[][]Button{btnArray1, btnArray3}}, 302 | want: 2, 303 | }, 304 | { 305 | name: "OK_0", 306 | fields: fields{[][]Button{}}, 307 | want: 0, 308 | }, 309 | } 310 | for _, tt := range tests { 311 | t.Run(tt.name, func(t *testing.T) { 312 | k := &Keyboard{ 313 | Rows: tt.fields.Rows, 314 | } 315 | if got := k.RowsCount(); got != tt.want { 316 | t.Errorf("RowsCount() = %v, want %v", got, tt.want) 317 | } 318 | }) 319 | } 320 | } 321 | 322 | func TestKeyboard_SwapRows(t *testing.T) { 323 | type args struct { 324 | first int 325 | second int 326 | } 327 | 328 | array1 := [][]Button{btnArray1, btnArray2, btnArray3} 329 | array2 := [][]Button{btnArray3, btnArray2, btnArray1} 330 | 331 | tests := []struct { 332 | name string 333 | fields fields 334 | args args 335 | exp fields 336 | wantErr bool 337 | }{ 338 | { 339 | name: "OK", 340 | fields: fields{array1}, 341 | args: args{0, 2}, 342 | exp: fields{array2}, 343 | wantErr: false, 344 | }, 345 | { 346 | name: "Error", 347 | fields: fields{array1}, 348 | args: args{0, 6}, 349 | exp: fields{array1}, 350 | wantErr: true, 351 | }, 352 | } 353 | for _, tt := range tests { 354 | t.Run(tt.name, func(t *testing.T) { 355 | k := &Keyboard{ 356 | Rows: tt.fields.Rows, 357 | } 358 | if err := k.SwapRows(tt.args.first, tt.args.second); (err != nil) != tt.wantErr { 359 | t.Errorf("SwapRows() error = %v, wantErr %v", err, tt.wantErr) 360 | } 361 | assert.Equal(t, tt.exp.Rows, k.GetKeyboard()) 362 | }) 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /message_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package botgolang 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson4086215fDecodeGithubComMailRuImBotGolang(in *jlexer.Lexer, out *ParentMessage) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeFieldName(false) 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "chatId": 40 | out.ChatID = string(in.String()) 41 | case "messageId": 42 | out.MsgID = int64(in.Int64()) 43 | case "type": 44 | out.Type = string(in.String()) 45 | default: 46 | in.SkipRecursive() 47 | } 48 | in.WantComma() 49 | } 50 | in.Delim('}') 51 | if isTopLevel { 52 | in.Consumed() 53 | } 54 | } 55 | func easyjson4086215fEncodeGithubComMailRuImBotGolang(out *jwriter.Writer, in ParentMessage) { 56 | out.RawByte('{') 57 | first := true 58 | _ = first 59 | { 60 | const prefix string = ",\"chatId\":" 61 | out.RawString(prefix[1:]) 62 | out.String(string(in.ChatID)) 63 | } 64 | { 65 | const prefix string = ",\"messageId\":" 66 | out.RawString(prefix) 67 | out.Int64(int64(in.MsgID)) 68 | } 69 | { 70 | const prefix string = ",\"type\":" 71 | out.RawString(prefix) 72 | out.String(string(in.Type)) 73 | } 74 | out.RawByte('}') 75 | } 76 | 77 | // MarshalJSON supports json.Marshaler interface 78 | func (v ParentMessage) MarshalJSON() ([]byte, error) { 79 | w := jwriter.Writer{} 80 | easyjson4086215fEncodeGithubComMailRuImBotGolang(&w, v) 81 | return w.Buffer.BuildBytes(), w.Error 82 | } 83 | 84 | // MarshalEasyJSON supports easyjson.Marshaler interface 85 | func (v ParentMessage) MarshalEasyJSON(w *jwriter.Writer) { 86 | easyjson4086215fEncodeGithubComMailRuImBotGolang(w, v) 87 | } 88 | 89 | // UnmarshalJSON supports json.Unmarshaler interface 90 | func (v *ParentMessage) UnmarshalJSON(data []byte) error { 91 | r := jlexer.Lexer{Data: data} 92 | easyjson4086215fDecodeGithubComMailRuImBotGolang(&r, v) 93 | return r.Error() 94 | } 95 | 96 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 97 | func (v *ParentMessage) UnmarshalEasyJSON(l *jlexer.Lexer) { 98 | easyjson4086215fDecodeGithubComMailRuImBotGolang(l, v) 99 | } 100 | func easyjson4086215fDecodeGithubComMailRuImBotGolang1(in *jlexer.Lexer, out *Message) { 101 | isTopLevel := in.IsStart() 102 | if in.IsNull() { 103 | if isTopLevel { 104 | in.Consumed() 105 | } 106 | in.Skip() 107 | return 108 | } 109 | in.Delim('{') 110 | for !in.IsDelim('}') { 111 | key := in.UnsafeFieldName(false) 112 | in.WantColon() 113 | if in.IsNull() { 114 | in.Skip() 115 | in.WantComma() 116 | continue 117 | } 118 | switch key { 119 | case "ContentType": 120 | out.ContentType = MessageContentType(in.Uint8()) 121 | case "msgId": 122 | out.ID = string(in.String()) 123 | case "fileId": 124 | out.FileID = string(in.String()) 125 | case "text": 126 | out.Text = string(in.String()) 127 | case "chat": 128 | (out.Chat).UnmarshalEasyJSON(in) 129 | case "replyMsgId": 130 | out.ReplyMsgID = string(in.String()) 131 | case "forwardMsgId": 132 | out.ForwardMsgID = string(in.String()) 133 | case "forwardChatId": 134 | out.ForwardChatID = string(in.String()) 135 | case "timestamp": 136 | out.Timestamp = int(in.Int()) 137 | case "parent_topic": 138 | if in.IsNull() { 139 | in.Skip() 140 | out.ParentMessage = nil 141 | } else { 142 | if out.ParentMessage == nil { 143 | out.ParentMessage = new(ParentMessage) 144 | } 145 | (*out.ParentMessage).UnmarshalEasyJSON(in) 146 | } 147 | case "inlineKeyboardMarkup": 148 | if in.IsNull() { 149 | in.Skip() 150 | out.InlineKeyboard = nil 151 | } else { 152 | if out.InlineKeyboard == nil { 153 | out.InlineKeyboard = new(Keyboard) 154 | } 155 | easyjson4086215fDecodeGithubComMailRuImBotGolang2(in, out.InlineKeyboard) 156 | } 157 | case "parseMode": 158 | out.ParseMode = ParseMode(in.String()) 159 | case "requestID": 160 | out.RequestID = string(in.String()) 161 | case "deeplink": 162 | out.Deeplink = string(in.String()) 163 | default: 164 | in.SkipRecursive() 165 | } 166 | in.WantComma() 167 | } 168 | in.Delim('}') 169 | if isTopLevel { 170 | in.Consumed() 171 | } 172 | } 173 | func easyjson4086215fEncodeGithubComMailRuImBotGolang1(out *jwriter.Writer, in Message) { 174 | out.RawByte('{') 175 | first := true 176 | _ = first 177 | { 178 | const prefix string = ",\"ContentType\":" 179 | out.RawString(prefix[1:]) 180 | out.Uint8(uint8(in.ContentType)) 181 | } 182 | { 183 | const prefix string = ",\"msgId\":" 184 | out.RawString(prefix) 185 | out.String(string(in.ID)) 186 | } 187 | { 188 | const prefix string = ",\"fileId\":" 189 | out.RawString(prefix) 190 | out.String(string(in.FileID)) 191 | } 192 | { 193 | const prefix string = ",\"text\":" 194 | out.RawString(prefix) 195 | out.String(string(in.Text)) 196 | } 197 | { 198 | const prefix string = ",\"chat\":" 199 | out.RawString(prefix) 200 | (in.Chat).MarshalEasyJSON(out) 201 | } 202 | { 203 | const prefix string = ",\"replyMsgId\":" 204 | out.RawString(prefix) 205 | out.String(string(in.ReplyMsgID)) 206 | } 207 | { 208 | const prefix string = ",\"forwardMsgId\":" 209 | out.RawString(prefix) 210 | out.String(string(in.ForwardMsgID)) 211 | } 212 | { 213 | const prefix string = ",\"forwardChatId\":" 214 | out.RawString(prefix) 215 | out.String(string(in.ForwardChatID)) 216 | } 217 | { 218 | const prefix string = ",\"timestamp\":" 219 | out.RawString(prefix) 220 | out.Int(int(in.Timestamp)) 221 | } 222 | { 223 | const prefix string = ",\"parent_topic\":" 224 | out.RawString(prefix) 225 | if in.ParentMessage == nil { 226 | out.RawString("null") 227 | } else { 228 | (*in.ParentMessage).MarshalEasyJSON(out) 229 | } 230 | } 231 | { 232 | const prefix string = ",\"inlineKeyboardMarkup\":" 233 | out.RawString(prefix) 234 | if in.InlineKeyboard == nil { 235 | out.RawString("null") 236 | } else { 237 | easyjson4086215fEncodeGithubComMailRuImBotGolang2(out, *in.InlineKeyboard) 238 | } 239 | } 240 | { 241 | const prefix string = ",\"parseMode\":" 242 | out.RawString(prefix) 243 | out.String(string(in.ParseMode)) 244 | } 245 | { 246 | const prefix string = ",\"requestID\":" 247 | out.RawString(prefix) 248 | out.String(string(in.RequestID)) 249 | } 250 | { 251 | const prefix string = ",\"deeplink\":" 252 | out.RawString(prefix) 253 | out.String(string(in.Deeplink)) 254 | } 255 | out.RawByte('}') 256 | } 257 | 258 | // MarshalJSON supports json.Marshaler interface 259 | func (v Message) MarshalJSON() ([]byte, error) { 260 | w := jwriter.Writer{} 261 | easyjson4086215fEncodeGithubComMailRuImBotGolang1(&w, v) 262 | return w.Buffer.BuildBytes(), w.Error 263 | } 264 | 265 | // MarshalEasyJSON supports easyjson.Marshaler interface 266 | func (v Message) MarshalEasyJSON(w *jwriter.Writer) { 267 | easyjson4086215fEncodeGithubComMailRuImBotGolang1(w, v) 268 | } 269 | 270 | // UnmarshalJSON supports json.Unmarshaler interface 271 | func (v *Message) UnmarshalJSON(data []byte) error { 272 | r := jlexer.Lexer{Data: data} 273 | easyjson4086215fDecodeGithubComMailRuImBotGolang1(&r, v) 274 | return r.Error() 275 | } 276 | 277 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 278 | func (v *Message) UnmarshalEasyJSON(l *jlexer.Lexer) { 279 | easyjson4086215fDecodeGithubComMailRuImBotGolang1(l, v) 280 | } 281 | func easyjson4086215fDecodeGithubComMailRuImBotGolang2(in *jlexer.Lexer, out *Keyboard) { 282 | isTopLevel := in.IsStart() 283 | if in.IsNull() { 284 | if isTopLevel { 285 | in.Consumed() 286 | } 287 | in.Skip() 288 | return 289 | } 290 | in.Delim('{') 291 | for !in.IsDelim('}') { 292 | key := in.UnsafeFieldName(false) 293 | in.WantColon() 294 | if in.IsNull() { 295 | in.Skip() 296 | in.WantComma() 297 | continue 298 | } 299 | switch key { 300 | case "Rows": 301 | if in.IsNull() { 302 | in.Skip() 303 | out.Rows = nil 304 | } else { 305 | in.Delim('[') 306 | if out.Rows == nil { 307 | if !in.IsDelim(']') { 308 | out.Rows = make([][]Button, 0, 2) 309 | } else { 310 | out.Rows = [][]Button{} 311 | } 312 | } else { 313 | out.Rows = (out.Rows)[:0] 314 | } 315 | for !in.IsDelim(']') { 316 | var v1 []Button 317 | if in.IsNull() { 318 | in.Skip() 319 | v1 = nil 320 | } else { 321 | in.Delim('[') 322 | if v1 == nil { 323 | if !in.IsDelim(']') { 324 | v1 = make([]Button, 0, 1) 325 | } else { 326 | v1 = []Button{} 327 | } 328 | } else { 329 | v1 = (v1)[:0] 330 | } 331 | for !in.IsDelim(']') { 332 | var v2 Button 333 | (v2).UnmarshalEasyJSON(in) 334 | v1 = append(v1, v2) 335 | in.WantComma() 336 | } 337 | in.Delim(']') 338 | } 339 | out.Rows = append(out.Rows, v1) 340 | in.WantComma() 341 | } 342 | in.Delim(']') 343 | } 344 | default: 345 | in.SkipRecursive() 346 | } 347 | in.WantComma() 348 | } 349 | in.Delim('}') 350 | if isTopLevel { 351 | in.Consumed() 352 | } 353 | } 354 | func easyjson4086215fEncodeGithubComMailRuImBotGolang2(out *jwriter.Writer, in Keyboard) { 355 | out.RawByte('{') 356 | first := true 357 | _ = first 358 | { 359 | const prefix string = ",\"Rows\":" 360 | out.RawString(prefix[1:]) 361 | if in.Rows == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 362 | out.RawString("null") 363 | } else { 364 | out.RawByte('[') 365 | for v3, v4 := range in.Rows { 366 | if v3 > 0 { 367 | out.RawByte(',') 368 | } 369 | if v4 == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 370 | out.RawString("null") 371 | } else { 372 | out.RawByte('[') 373 | for v5, v6 := range v4 { 374 | if v5 > 0 { 375 | out.RawByte(',') 376 | } 377 | (v6).MarshalEasyJSON(out) 378 | } 379 | out.RawByte(']') 380 | } 381 | } 382 | out.RawByte(']') 383 | } 384 | } 385 | out.RawByte('}') 386 | } 387 | -------------------------------------------------------------------------------- /bot.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | /* 4 | 💥 botgolang is zero-configuration library with convenient interface. 5 | Crafted with love in @mail for your awesome bots. 6 | */ 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "net/http" 12 | "os" 13 | 14 | "github.com/sirupsen/logrus" 15 | ) 16 | 17 | const ( 18 | defaultAPIURL = "https://api.icq.net/bot/v1" 19 | defaultDebug = false 20 | ) 21 | 22 | // Bot is the main structure for interaction with API. 23 | // All fields are private, you can configure bot using config arguments in NewBot func. 24 | type Bot struct { 25 | client *Client 26 | updater *Updater 27 | logger *logrus.Logger 28 | Info *BotInfo 29 | } 30 | 31 | // AutosubscribeToThreads toggles thread auto-subscription behaviour for the specified chat. 32 | // enable – turn the feature on/off. 33 | // withExisting – if true, the bot will also subscribe to already existing threads. 34 | func (b *Bot) AutosubscribeToThreads(chatID string, enable, withExisting bool) error { 35 | return b.client.AutosubscribeToThreads(chatID, enable, withExisting) 36 | } 37 | 38 | // AddThread adds a new thread to the specified chat and returns the thread ID. 39 | func (b *Bot) AddThread(chatID, msgID string) (*Thread, error) { 40 | return b.client.AddThread(chatID, msgID) 41 | } 42 | 43 | // GetThreadSubscribers gets the subscribers list for a thread. 44 | // Either cursor or pageSize must be provided. 45 | func (b *Bot) GetThreadSubscribers(threadID string, cursor string, pageSize int) (*ThreadSubscribers, error) { 46 | return b.client.GetThreadSubscribers(threadID, cursor, pageSize) 47 | } 48 | 49 | // GetInfo returns information about bot: 50 | // id, name, about, avatar 51 | func (b *Bot) GetInfo() (*BotInfo, error) { 52 | return b.client.GetInfo() 53 | } 54 | 55 | // GetChatInfo returns information about chat: 56 | // id, type, title, public, group, inviteLink, admins 57 | func (b *Bot) GetChatInfo(chatID string) (*Chat, error) { 58 | return b.client.GetChatInfo(chatID) 59 | } 60 | 61 | // SendChatActions sends an actions like "typing, looking" 62 | func (b *Bot) SendChatActions(chatID string, actions ...ChatAction) error { 63 | return b.client.SendChatActions(chatID, actions...) 64 | } 65 | 66 | // GetChatAdmins returns chat admins list with fields: 67 | // userID, creator flag 68 | func (b *Bot) GetChatAdmins(chatID string) ([]ChatMember, error) { 69 | return b.client.GetChatAdmins(chatID) 70 | } 71 | 72 | // GetChatMem returns chat members list with fields: 73 | // userID, creator flag, admin flag 74 | func (b *Bot) GetChatMembers(chatID string) ([]ChatMember, error) { 75 | return b.client.GetChatMembers(chatID) 76 | } 77 | 78 | // GetChatBlockedUsers returns chat blocked users list: 79 | // userID 80 | func (b *Bot) GetChatBlockedUsers(chatID string) ([]User, error) { 81 | return b.client.GetChatBlockedUsers(chatID) 82 | } 83 | 84 | // GetChatPendingUsers returns chat join pending users list: 85 | // userID 86 | func (b *Bot) GetChatPendingUsers(chatID string) ([]User, error) { 87 | return b.client.GetChatPendingUsers(chatID) 88 | } 89 | 90 | // BlockChatUser blocks user and removes him from chat. 91 | // If deleteLastMessages is true, the messages written recently will be deleted 92 | func (b *Bot) BlockChatUser(chatID, userID string, deleteLastMessages bool) error { 93 | return b.client.BlockChatUser(chatID, userID, deleteLastMessages) 94 | } 95 | 96 | // UnblockChatUser unblocks user in chat 97 | func (b *Bot) UnblockChatUser(chatID, userID string) error { 98 | return b.client.UnblockChatUser(chatID, userID) 99 | } 100 | 101 | // DeleteChatMembers removes multiple members from chat 102 | func (b *Bot) DeleteChatMembers(chatID string, members []string) error { 103 | return b.client.DeleteChatMembers(chatID, members) 104 | } 105 | 106 | // AddChatMembers adds multiple members to chat 107 | func (b *Bot) AddChatMembers(chatID string, members []string) error { 108 | return b.client.AddChatMembers(chatID, members) 109 | } 110 | 111 | // ResolveChatJoinRequests resolves pending join requests for specified user or all pending users 112 | func (b *Bot) ResolveChatJoinRequests(chatID, userID string, accept, everyone bool) error { 113 | return b.client.ResolveChatPending(chatID, userID, accept, everyone) 114 | } 115 | 116 | // SetChatTitle changes chat title 117 | func (b *Bot) SetChatTitle(chatID, title string) error { 118 | return b.client.SetChatTitle(chatID, title) 119 | } 120 | 121 | // SetChatAbout changes chat about 122 | func (b *Bot) SetChatAbout(chatID, about string) error { 123 | return b.client.SetChatAbout(chatID, about) 124 | } 125 | 126 | // SetChatRules changes chat rules 127 | func (b *Bot) SetChatRules(chatID, rules string) error { 128 | return b.client.SetChatRules(chatID, rules) 129 | } 130 | 131 | // GetFileInfo returns information about file: 132 | // id, type, size, filename, url 133 | func (b *Bot) GetFileInfo(fileID string) (*File, error) { 134 | return b.client.GetFileInfo(fileID) 135 | } 136 | 137 | // NewMessage returns new message 138 | func (b *Bot) NewMessage(chatID string) *Message { 139 | return &Message{ 140 | client: b.client, 141 | Chat: Chat{ID: chatID}, 142 | } 143 | } 144 | 145 | // NewTextMessage returns new text message 146 | func (b *Bot) NewTextMessage(chatID, text string) *Message { 147 | return &Message{ 148 | client: b.client, 149 | Chat: Chat{ID: chatID}, 150 | Text: text, 151 | ContentType: Text, 152 | } 153 | } 154 | 155 | // NewTextMessageWithRequestID returns new text message with client requestID 156 | func (b *Bot) NewTextMessageWithRequestID(chatID, text, requestID string) *Message { 157 | return &Message{ 158 | client: b.client, 159 | Chat: Chat{ID: chatID}, 160 | Text: text, 161 | ContentType: Text, 162 | RequestID: requestID, 163 | } 164 | } 165 | 166 | // NewInlineKeyboardMessage returns new text message with inline keyboard 167 | func (b *Bot) NewInlineKeyboardMessage(chatID, text string, keyboard Keyboard) *Message { 168 | return &Message{ 169 | client: b.client, 170 | Chat: Chat{ID: chatID}, 171 | Text: text, 172 | ContentType: Text, 173 | InlineKeyboard: &keyboard, 174 | } 175 | } 176 | 177 | // NewDeeplinkMessage returns new text message with inline keyboard and deeplink 178 | func (b *Bot) NewDeeplinkMessage(chatID, text string, keyboard Keyboard, deeplink string) *Message { 179 | return &Message{ 180 | client: b.client, 181 | Chat: Chat{ID: chatID}, 182 | Text: text, 183 | ContentType: Deeplink, 184 | InlineKeyboard: &keyboard, 185 | Deeplink: deeplink, 186 | } 187 | } 188 | 189 | // NewFileMessage returns new file message 190 | func (b *Bot) NewFileMessage(chatID string, file *os.File) *Message { 191 | return &Message{ 192 | client: b.client, 193 | Chat: Chat{ID: chatID}, 194 | File: file, 195 | ContentType: OtherFile, 196 | } 197 | } 198 | 199 | // NewFileMessageByFileID returns new message with previously uploaded file id 200 | func (b *Bot) NewFileMessageByFileID(chatID, fileID string) *Message { 201 | return &Message{ 202 | client: b.client, 203 | Chat: Chat{ID: chatID}, 204 | FileID: fileID, 205 | ContentType: OtherFile, 206 | } 207 | } 208 | 209 | // NewVoiceMessage returns new voice message 210 | func (b *Bot) NewVoiceMessage(chatID string, file *os.File) *Message { 211 | return &Message{ 212 | client: b.client, 213 | Chat: Chat{ID: chatID}, 214 | File: file, 215 | ContentType: Voice, 216 | } 217 | } 218 | 219 | // NewVoiceMessageByFileID returns new message with previously uploaded voice file id 220 | func (b *Bot) NewVoiceMessageByFileID(chatID, fileID string) *Message { 221 | return &Message{ 222 | client: b.client, 223 | Chat: Chat{ID: chatID}, 224 | FileID: fileID, 225 | ContentType: Voice, 226 | } 227 | } 228 | 229 | // NewMessageFromPart returns new message based on part message 230 | func (b *Bot) NewMessageFromPart(message PartMessage) *Message { 231 | return &Message{ 232 | client: b.client, 233 | ID: message.MsgID, 234 | Chat: Chat{ID: message.From.User.ID, Title: message.From.FirstName}, 235 | Text: message.Text, 236 | Timestamp: message.Timestamp, 237 | } 238 | } 239 | 240 | // NewButtonResponse returns new ButtonResponse 241 | func (b *Bot) NewButtonResponse(queryID, url, text string, showAlert bool) *ButtonResponse { 242 | return &ButtonResponse{ 243 | client: b.client, 244 | QueryID: queryID, 245 | Text: text, 246 | URL: url, 247 | ShowAlert: showAlert, 248 | } 249 | } 250 | 251 | func (b *Bot) NewChat(id string) *Chat { 252 | return &Chat{ 253 | client: b.client, 254 | ID: id, 255 | } 256 | } 257 | 258 | // SendMessage sends a message, passed as an argument. 259 | // This method fills the argument with ID of sent message and returns an error if any. 260 | func (b *Bot) SendMessage(message *Message) error { 261 | message.client = b.client 262 | return message.Send() 263 | } 264 | 265 | // EditMessage edit a message passed as an argument. 266 | func (b *Bot) EditMessage(message *Message) error { 267 | return b.client.EditMessage(message) 268 | } 269 | 270 | // GetUpdatesChannel returns a channel, which will be filled with events. 271 | // You can pass cancellable context there and stop receiving events. 272 | // The channel will be closed after context cancellation. 273 | func (b *Bot) GetUpdatesChannel(ctx context.Context) <-chan Event { 274 | updates := make(chan Event) 275 | 276 | go b.updater.RunUpdatesCheck(ctx, updates) 277 | 278 | return updates 279 | } 280 | 281 | // NewBot returns new bot object. 282 | // All communications with bot API must go through Bot struct. 283 | // In general you don't need to configure this bot, therefore all options are optional arguments. 284 | func NewBot(token string, opts ...BotOption) (*Bot, error) { 285 | logger := logrus.New() 286 | logger.SetFormatter(&logrus.TextFormatter{ 287 | FullTimestamp: true, 288 | TimestampFormat: "2006-01-02 15:04:05", 289 | }) 290 | 291 | apiURL := defaultAPIURL 292 | debug := defaultDebug 293 | client := *http.DefaultClient 294 | for _, option := range opts { 295 | switch option.Type() { 296 | case "api_url": 297 | apiURL = option.Value().(string) 298 | case "debug": 299 | debug = option.Value().(bool) 300 | case "http_client": 301 | client = option.Value().(http.Client) 302 | } 303 | } 304 | 305 | if debug { 306 | logger.SetLevel(logrus.DebugLevel) 307 | } 308 | 309 | tgClient := NewCustomClient(&client, apiURL, token, logger) 310 | updater := NewUpdater(tgClient, 0, logger) 311 | 312 | info, err := tgClient.GetInfo() 313 | if err != nil { 314 | return nil, fmt.Errorf("cannot get info about bot: %s", err) 315 | } 316 | 317 | return &Bot{ 318 | client: tgClient, 319 | updater: updater, 320 | logger: logger, 321 | Info: info, 322 | }, nil 323 | } 324 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package botgolang 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "fmt" 8 | "io" 9 | "mime/multipart" 10 | "net/http" 11 | "net/url" 12 | "os" 13 | "strconv" 14 | 15 | "github.com/sirupsen/logrus" 16 | ) 17 | 18 | type Client struct { 19 | client *http.Client 20 | token string 21 | baseURL string 22 | logger *logrus.Logger 23 | } 24 | 25 | func (c *Client) Do(path string, params url.Values, file *os.File) ([]byte, error) { 26 | return c.DoWithContext(context.Background(), path, params, file) 27 | } 28 | 29 | func (c *Client) DoWithContext(ctx context.Context, path string, params url.Values, file *os.File) ([]byte, error) { 30 | apiURL, err := url.Parse(c.baseURL + path) 31 | params.Set("token", c.token) 32 | 33 | if err != nil { 34 | return nil, fmt.Errorf("cannot parse url: %s", err) 35 | } 36 | 37 | apiURL.RawQuery = params.Encode() 38 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL.String(), nil) 39 | if err != nil || req == nil { 40 | return nil, fmt.Errorf("cannot init http request: %s", err) 41 | } 42 | 43 | if file != nil { 44 | buffer := &bytes.Buffer{} 45 | multipartWriter := multipart.NewWriter(buffer) 46 | 47 | fileWriter, err := multipartWriter.CreateFormFile("file", file.Name()) 48 | if err != nil { 49 | return nil, fmt.Errorf("cannot create multipart writer: %s", err) 50 | } 51 | 52 | _, err = io.Copy(fileWriter, file) 53 | if err != nil { 54 | return nil, fmt.Errorf("cannot copy file into buffer: %s", err) 55 | } 56 | 57 | if err := multipartWriter.Close(); err != nil { 58 | return nil, fmt.Errorf("cannot close multipartWriter: %s", err) 59 | } 60 | 61 | req.Header.Set("Content-Type", multipartWriter.FormDataContentType()) 62 | req.Body = io.NopCloser(buffer) 63 | req.Method = http.MethodPost 64 | } 65 | 66 | c.logger.WithFields(logrus.Fields{ 67 | "api_url": apiURL, 68 | }).Debug("requesting api") 69 | 70 | resp, err := c.client.Do(req) 71 | if err != nil { 72 | c.logger.WithFields(logrus.Fields{ 73 | "err": err, 74 | }).Error("request error") 75 | return []byte{}, fmt.Errorf("cannot make request to bot api: %s", err) 76 | } 77 | 78 | defer func() { 79 | if err := resp.Body.Close(); err != nil { 80 | c.logger.WithFields(logrus.Fields{ 81 | "err": err, 82 | }).Error("cannot close body") 83 | } 84 | }() 85 | 86 | responseBody, err := io.ReadAll(resp.Body) 87 | if err != nil { 88 | c.logger.WithFields(logrus.Fields{ 89 | "err": err, 90 | }).Error("cannot read body") 91 | return []byte{}, fmt.Errorf("cannot read body: %s", err) 92 | } 93 | 94 | if c.logger.IsLevelEnabled(logrus.DebugLevel) { 95 | c.logger.WithFields(logrus.Fields{ 96 | "response": responseBody, 97 | }).Debug("got response from API") 98 | } 99 | 100 | if resp.StatusCode != http.StatusOK { 101 | return nil, fmt.Errorf("error status from API: %s", resp.Status) 102 | } 103 | 104 | response := &Response{} 105 | 106 | if err := json.Unmarshal(responseBody, response); err != nil { 107 | return nil, fmt.Errorf("cannot unmarshal json: %s", err) 108 | } 109 | 110 | if !response.OK { 111 | return responseBody, fmt.Errorf("error status from API: %s", response.Description) 112 | } 113 | 114 | return responseBody, nil 115 | } 116 | 117 | func (c *Client) AutosubscribeToThreads(chatID string, enable, withExisting bool) error { 118 | if chatID == "" { 119 | return fmt.Errorf("chatID cannot be empty") 120 | } 121 | 122 | params := url.Values{ 123 | "chatId": {chatID}, 124 | "enable": {strconv.FormatBool(enable)}, 125 | "withExisting": {strconv.FormatBool(withExisting)}, 126 | } 127 | 128 | if _, err := c.Do("/threads/autosubscribe", params, nil); err != nil { 129 | return fmt.Errorf("error while requesting threads autosubscribe: %w", err) 130 | } 131 | 132 | return nil 133 | } 134 | 135 | func (c *Client) AddThread(chatID, msgID string) (*Thread, error) { 136 | if chatID == "" { 137 | return nil, fmt.Errorf("chatID cannot be empty") 138 | } 139 | if msgID == "" { 140 | return nil, fmt.Errorf("msgID cannot be empty") 141 | } 142 | 143 | params := url.Values{ 144 | "chatId": {chatID}, 145 | "msgId": {msgID}, 146 | } 147 | 148 | response, err := c.Do("/threads/add", params, nil) 149 | if err != nil { 150 | return nil, fmt.Errorf("error while adding thread: %w", err) 151 | } 152 | 153 | thread := &Thread{} 154 | if err := json.Unmarshal(response, thread); err != nil { 155 | return nil, fmt.Errorf("error while unmarshalling thread response: %w", err) 156 | } 157 | 158 | return thread, nil 159 | } 160 | 161 | func (c *Client) GetThreadSubscribers(threadID string, cursor string, pageSize int) (*ThreadSubscribers, error) { 162 | if threadID == "" { 163 | return nil, fmt.Errorf("threadID cannot be empty") 164 | } 165 | 166 | params := url.Values{ 167 | "threadId": {threadID}, 168 | } 169 | 170 | if cursor != "" { 171 | params.Set("cursor", cursor) 172 | } 173 | if pageSize > 0 { 174 | params.Set("pageSize", strconv.Itoa(pageSize)) 175 | } 176 | 177 | response, err := c.Do("/threads/subscribers/get", params, nil) 178 | if err != nil { 179 | return nil, fmt.Errorf("error while getting thread subscribers: %w", err) 180 | } 181 | 182 | threadSubscribers := &ThreadSubscribers{} 183 | if err := json.Unmarshal(response, threadSubscribers); err != nil { 184 | return nil, fmt.Errorf("error while unmarshalling thread subscribers response: %w", err) 185 | } 186 | 187 | return threadSubscribers, nil 188 | } 189 | 190 | func (c *Client) GetInfo() (*BotInfo, error) { 191 | response, err := c.Do("/self/get", url.Values{}, nil) 192 | if err != nil { 193 | return nil, fmt.Errorf("error while receiving information: %s", err) 194 | } 195 | 196 | info := &BotInfo{} 197 | if err := json.Unmarshal(response, info); err != nil { 198 | return nil, fmt.Errorf("error while unmarshalling information: %s", err) 199 | } 200 | 201 | return info, nil 202 | } 203 | 204 | func (c *Client) GetChatInfo(chatID string) (*Chat, error) { 205 | if chatID == "" { 206 | return nil, fmt.Errorf("chatID cannot be empty") 207 | } 208 | 209 | params := url.Values{ 210 | "chatId": {chatID}, 211 | } 212 | response, err := c.Do("/chats/getInfo", params, nil) 213 | if err != nil { 214 | return nil, fmt.Errorf("error while receiving information: %s", err) 215 | } 216 | 217 | chat := &Chat{ 218 | client: c, 219 | ID: chatID, 220 | } 221 | if err := json.Unmarshal(response, chat); err != nil { 222 | return nil, fmt.Errorf("error while unmarshalling information: %s", err) 223 | } 224 | 225 | if chat.Type == Private { 226 | return chat, nil 227 | } 228 | return chat, nil 229 | } 230 | 231 | func (c *Client) SendChatActions(chatID string, actions ...ChatAction) error { 232 | if chatID == "" { 233 | return fmt.Errorf("chatID cannot be empty") 234 | } 235 | if len(actions) == 0 { 236 | return fmt.Errorf("actions cannot be empty") 237 | } 238 | 239 | actionsMap := make(map[ChatAction]bool) 240 | filteredActions := make([]ChatAction, 0) 241 | for _, action := range actions { 242 | if _, has := actionsMap[action]; !has { 243 | filteredActions = append(filteredActions, action) 244 | actionsMap[action] = true 245 | } 246 | } 247 | params := url.Values{ 248 | "chatId": {chatID}, 249 | "actions": filteredActions, 250 | } 251 | _, err := c.Do("/chats/sendActions", params, nil) 252 | if err != nil { 253 | return fmt.Errorf("error while receiving information: %s", err) 254 | } 255 | return nil 256 | } 257 | 258 | func (c *Client) GetChatAdmins(chatID string) ([]ChatMember, error) { 259 | if chatID == "" { 260 | return nil, fmt.Errorf("chatID cannot be empty") 261 | } 262 | 263 | params := url.Values{ 264 | "chatId": {chatID}, 265 | } 266 | 267 | response, err := c.Do("/chats/getAdmins", params, nil) 268 | if err != nil { 269 | return nil, fmt.Errorf("error while receiving admins: %s", err) 270 | } 271 | 272 | admins := new(AdminsListResponse) 273 | if err := json.Unmarshal(response, admins); err != nil { 274 | return nil, fmt.Errorf("error while unmarshalling admins: %s", err) 275 | } 276 | return admins.List, nil 277 | } 278 | 279 | func (c *Client) GetChatMembers(chatID string) ([]ChatMember, error) { 280 | if chatID == "" { 281 | return nil, fmt.Errorf("chatID cannot be empty") 282 | } 283 | 284 | params := url.Values{ 285 | "chatId": {chatID}, 286 | } 287 | 288 | response, err := c.Do("/chats/getMembers", params, nil) 289 | if err != nil { 290 | return nil, fmt.Errorf("error while receiving members: %s", err) 291 | } 292 | 293 | members := new(MembersListResponse) 294 | if err := json.Unmarshal(response, members); err != nil { 295 | return nil, fmt.Errorf("error while unmarshalling members: %s", err) 296 | } 297 | return members.List, nil 298 | } 299 | 300 | func (c *Client) GetChatBlockedUsers(chatID string) ([]User, error) { 301 | if chatID == "" { 302 | return nil, fmt.Errorf("chatID cannot be empty") 303 | } 304 | 305 | params := url.Values{ 306 | "chatId": {chatID}, 307 | } 308 | 309 | response, err := c.Do("/chats/getBlockedUsers", params, nil) 310 | if err != nil { 311 | return nil, fmt.Errorf("error while receiving blocked users: %s", err) 312 | } 313 | 314 | users := new(UsersListResponse) 315 | if err := json.Unmarshal(response, users); err != nil { 316 | return nil, fmt.Errorf("error while unmarshalling blocked users: %s", err) 317 | } 318 | return users.List, nil 319 | } 320 | 321 | func (c *Client) GetChatPendingUsers(chatID string) ([]User, error) { 322 | if chatID == "" { 323 | return nil, fmt.Errorf("chatID cannot be empty") 324 | } 325 | 326 | params := url.Values{ 327 | "chatId": {chatID}, 328 | } 329 | 330 | response, err := c.Do("/chats/getPendingUsers", params, nil) 331 | if err != nil { 332 | return nil, fmt.Errorf("error while receiving pending users: %s", err) 333 | } 334 | 335 | users := new(UsersListResponse) 336 | if err := json.Unmarshal(response, users); err != nil { 337 | return nil, fmt.Errorf("error while unmarshalling pending users: %s", err) 338 | } 339 | return users.List, nil 340 | } 341 | 342 | func (c *Client) BlockChatUser(chatID, userID string, deleteLastMessages bool) error { 343 | if chatID == "" { 344 | return fmt.Errorf("chatID cannot be empty") 345 | } 346 | if userID == "" { 347 | return fmt.Errorf("userID cannot be empty") 348 | } 349 | 350 | params := url.Values{ 351 | "chatId": {chatID}, 352 | "userId": {userID}, 353 | "delLastMessages": {strconv.FormatBool(deleteLastMessages)}, 354 | } 355 | 356 | response, err := c.Do("/chats/blockUser", params, nil) 357 | if err != nil { 358 | return fmt.Errorf("error while blocking user: %s", err) 359 | } 360 | 361 | users := new(UsersListResponse) 362 | if err := json.Unmarshal(response, users); err != nil { 363 | return fmt.Errorf("error while blocking user: %s", err) 364 | } 365 | return nil 366 | } 367 | 368 | func (c *Client) UnblockChatUser(chatID, userID string) error { 369 | if chatID == "" { 370 | return fmt.Errorf("chatID cannot be empty") 371 | } 372 | if userID == "" { 373 | return fmt.Errorf("userID cannot be empty") 374 | } 375 | 376 | params := url.Values{ 377 | "chatId": {chatID}, 378 | "userId": {userID}, 379 | } 380 | 381 | response, err := c.Do("/chats/unblockUser", params, nil) 382 | if err != nil { 383 | return fmt.Errorf("error while unblocking user: %s", err) 384 | } 385 | 386 | users := new(UsersListResponse) 387 | if err := json.Unmarshal(response, users); err != nil { 388 | return fmt.Errorf("error while unblocking user: %s", err) 389 | } 390 | return nil 391 | } 392 | 393 | func (c *Client) ResolveChatPending(chatID, userID string, approve, everyone bool) error { 394 | if chatID == "" { 395 | return fmt.Errorf("chatID cannot be empty") 396 | } 397 | 398 | params := url.Values{ 399 | "chatId": {chatID}, 400 | "approve": {strconv.FormatBool(approve)}, 401 | } 402 | if everyone { 403 | params.Set("everyone", "true") 404 | } else { 405 | params.Set("userId", userID) 406 | } 407 | 408 | if _, err := c.Do("/chats/resolvePending", params, nil); err != nil { 409 | return fmt.Errorf("error while resolving chat pendings: %s", err) 410 | } 411 | return nil 412 | } 413 | 414 | func (c *Client) DeleteChatMembers(chatID string, members []string) error { 415 | if chatID == "" { 416 | return fmt.Errorf("chatID cannot be empty") 417 | } 418 | if len(members) == 0 { 419 | return fmt.Errorf("members list cannot be empty") 420 | } 421 | 422 | membersList := make([]map[string]string, len(members)) 423 | for i, member := range members { 424 | membersList[i] = map[string]string{"sn": member} 425 | } 426 | 427 | membersJSON, err := json.Marshal(membersList) 428 | if err != nil { 429 | return fmt.Errorf("error while marshalling members list: %s", err) 430 | } 431 | 432 | params := url.Values{ 433 | "chatId": {chatID}, 434 | "members": {string(membersJSON)}, 435 | } 436 | 437 | if _, err := c.Do("/chats/members/delete", params, nil); err != nil { 438 | return fmt.Errorf("error while deleting chat members: %s", err) 439 | } 440 | return nil 441 | } 442 | 443 | func (c *Client) AddChatMembers(chatID string, members []string) error { 444 | if chatID == "" { 445 | return fmt.Errorf("chatID cannot be empty") 446 | } 447 | if len(members) == 0 { 448 | return fmt.Errorf("members list cannot be empty") 449 | } 450 | 451 | membersList := make([]map[string]string, len(members)) 452 | for i, member := range members { 453 | membersList[i] = map[string]string{"sn": member} 454 | } 455 | 456 | membersJSON, err := json.Marshal(membersList) 457 | if err != nil { 458 | return fmt.Errorf("error while marshalling members list: %s", err) 459 | } 460 | 461 | params := url.Values{ 462 | "chatId": {chatID}, 463 | "members": {string(membersJSON)}, 464 | } 465 | 466 | if _, err := c.Do("/chats/members/add", params, nil); err != nil { 467 | return fmt.Errorf("error while adding chat members: %s", err) 468 | } 469 | return nil 470 | } 471 | 472 | func (c *Client) SetChatTitle(chatID, title string) error { 473 | if chatID == "" { 474 | return fmt.Errorf("chatID cannot be empty") 475 | } 476 | if title == "" { 477 | return fmt.Errorf("title cannot be empty") 478 | } 479 | 480 | params := url.Values{ 481 | "chatId": {chatID}, 482 | "title": {title}, 483 | } 484 | 485 | if _, err := c.Do("/chats/setTitle", params, nil); err != nil { 486 | return fmt.Errorf("error while setting chat title: %s", err) 487 | } 488 | return nil 489 | } 490 | 491 | func (c *Client) SetChatAbout(chatID, about string) error { 492 | if chatID == "" { 493 | return fmt.Errorf("chatID cannot be empty") 494 | } 495 | 496 | params := url.Values{ 497 | "chatId": {chatID}, 498 | "about": {about}, 499 | } 500 | 501 | if _, err := c.Do("/chats/setAbout", params, nil); err != nil { 502 | return fmt.Errorf("error while setting chat about: %s", err) 503 | } 504 | return nil 505 | } 506 | 507 | func (c *Client) SetChatRules(chatID, rules string) error { 508 | if chatID == "" { 509 | return fmt.Errorf("chatID cannot be empty") 510 | } 511 | 512 | params := url.Values{ 513 | "chatId": {chatID}, 514 | "rules": {rules}, 515 | } 516 | 517 | if _, err := c.Do("/chats/setRules", params, nil); err != nil { 518 | return fmt.Errorf("error while setting chat rules: %s", err) 519 | } 520 | return nil 521 | } 522 | 523 | func (c *Client) GetFileInfo(fileID string) (*File, error) { 524 | if fileID == "" { 525 | return nil, fmt.Errorf("fileID cannot be empty") 526 | } 527 | 528 | params := url.Values{ 529 | "fileId": {fileID}, 530 | } 531 | response, err := c.Do("/files/getInfo", params, nil) 532 | if err != nil { 533 | return nil, fmt.Errorf("error while receiving information: %s", err) 534 | } 535 | 536 | file := &File{} 537 | if err := json.Unmarshal(response, file); err != nil { 538 | return nil, fmt.Errorf("error while unmarshalling information: %s", err) 539 | } 540 | 541 | return file, nil 542 | } 543 | 544 | func (c *Client) GetVoiceInfo(fileID string) (*File, error) { 545 | return c.GetFileInfo(fileID) 546 | } 547 | 548 | func (c *Client) SendTextMessage(message *Message) error { 549 | if message == nil { 550 | return fmt.Errorf("message cannot be nil") 551 | } 552 | if message.Chat.ID == "" { 553 | return fmt.Errorf("chatID cannot be empty") 554 | } 555 | if message.Text == "" { 556 | return fmt.Errorf("text cannot be empty") 557 | } 558 | 559 | params := url.Values{ 560 | "chatId": {message.Chat.ID}, 561 | "text": {message.Text}, 562 | "request-id": {message.RequestID}, 563 | } 564 | 565 | if message.ReplyMsgID != "" { 566 | params.Set("replyMsgId", message.ReplyMsgID) 567 | } 568 | 569 | if message.ForwardMsgID != "" { 570 | params.Set("forwardMsgId", message.ForwardMsgID) 571 | params.Set("forwardChatId", message.ForwardChatID) 572 | } 573 | 574 | if message.InlineKeyboard != nil { 575 | data, err := json.Marshal(message.InlineKeyboard.GetKeyboard()) 576 | if err != nil { 577 | return fmt.Errorf("cannot marshal inline keyboard markup: %s", err) 578 | } 579 | 580 | params.Set("inlineKeyboardMarkup", string(data)) 581 | } 582 | 583 | if message.ParseMode != "" { 584 | params.Set("parseMode", string(message.ParseMode)) 585 | } 586 | 587 | response, err := c.Do("/messages/sendText", params, nil) 588 | if err != nil { 589 | return fmt.Errorf("error while sending text: %s", err) 590 | } 591 | 592 | if err := json.Unmarshal(response, message); err != nil { 593 | return fmt.Errorf("cannot unmarshal response from API: %s", err) 594 | } 595 | 596 | return nil 597 | } 598 | 599 | func (c *Client) SendTextWithDeeplinkMessage(message *Message) error { 600 | if message == nil { 601 | return fmt.Errorf("message cannot be nil") 602 | } 603 | if message.Chat.ID == "" { 604 | return fmt.Errorf("chatID cannot be empty") 605 | } 606 | if message.Text == "" { 607 | return fmt.Errorf("text cannot be empty") 608 | } 609 | 610 | params := url.Values{ 611 | "chatId": {message.Chat.ID}, 612 | "text": {message.Text}, 613 | "request-id": {message.RequestID}, 614 | } 615 | 616 | if message.ReplyMsgID != "" { 617 | params.Set("replyMsgId", message.ReplyMsgID) 618 | } 619 | 620 | if message.ForwardMsgID != "" { 621 | params.Set("forwardMsgId", message.ForwardMsgID) 622 | params.Set("forwardChatId", message.ForwardChatID) 623 | } 624 | 625 | if message.InlineKeyboard != nil { 626 | data, err := json.Marshal(message.InlineKeyboard.GetKeyboard()) 627 | if err != nil { 628 | return fmt.Errorf("cannot marshal inline keyboard markup: %s", err) 629 | } 630 | 631 | params.Set("inlineKeyboardMarkup", string(data)) 632 | } 633 | 634 | if len(message.Deeplink) == 0 { 635 | return fmt.Errorf("deeplink can't be empty for SendTextWithDeeplink") 636 | } 637 | params.Set("deeplink", message.Deeplink) 638 | 639 | if message.ParseMode != "" { 640 | params.Set("parseMode", string(message.ParseMode)) 641 | } 642 | 643 | response, err := c.Do("/messages/sendTextWithDeeplink", params, nil) 644 | if err != nil { 645 | return fmt.Errorf("error while sending text: %s", err) 646 | } 647 | 648 | if err := json.Unmarshal(response, message); err != nil { 649 | return fmt.Errorf("cannot unmarshal response from API: %s", err) 650 | } 651 | 652 | return nil 653 | } 654 | 655 | func (c *Client) EditMessage(message *Message) error { 656 | if message == nil { 657 | return fmt.Errorf("message cannot be nil") 658 | } 659 | if message.ID == "" { 660 | return fmt.Errorf("message ID cannot be empty") 661 | } 662 | if message.Chat.ID == "" { 663 | return fmt.Errorf("chatID cannot be empty") 664 | } 665 | if message.Text == "" { 666 | return fmt.Errorf("text cannot be empty") 667 | } 668 | 669 | params := url.Values{ 670 | "msgId": {message.ID}, 671 | "chatId": {message.Chat.ID}, 672 | "text": {message.Text}, 673 | } 674 | 675 | if message.InlineKeyboard != nil { 676 | data, err := json.Marshal(message.InlineKeyboard.GetKeyboard()) 677 | if err != nil { 678 | return fmt.Errorf("cannot marshal inline keyboard markup: %s", err) 679 | } 680 | 681 | params.Set("inlineKeyboardMarkup", string(data)) 682 | } 683 | 684 | if message.ParseMode != "" { 685 | params.Set("parseMode", string(message.ParseMode)) 686 | } 687 | 688 | response, err := c.Do("/messages/editText", params, nil) 689 | if err != nil { 690 | return fmt.Errorf("error while editing text: %s", err) 691 | } 692 | 693 | if err := json.Unmarshal(response, message); err != nil { 694 | return fmt.Errorf("cannot unmarshal response from API: %s", err) 695 | } 696 | 697 | return nil 698 | } 699 | 700 | func (c *Client) DeleteMessage(message *Message) error { 701 | if message == nil { 702 | return fmt.Errorf("message cannot be nil") 703 | } 704 | if message.ID == "" { 705 | return fmt.Errorf("message ID cannot be empty") 706 | } 707 | if message.Chat.ID == "" { 708 | return fmt.Errorf("chatID cannot be empty") 709 | } 710 | 711 | params := url.Values{ 712 | "msgId": {message.ID}, 713 | "chatId": {message.Chat.ID}, 714 | } 715 | _, err := c.Do("/messages/deleteMessages", params, nil) 716 | if err != nil { 717 | return fmt.Errorf("error while deleting message: %s", err) 718 | } 719 | 720 | return nil 721 | } 722 | 723 | func (c *Client) SendFileMessage(message *Message) error { 724 | if message == nil { 725 | return fmt.Errorf("message cannot be nil") 726 | } 727 | if message.Chat.ID == "" { 728 | return fmt.Errorf("chatID cannot be empty") 729 | } 730 | if message.FileID == "" { 731 | return fmt.Errorf("fileID cannot be empty") 732 | } 733 | 734 | params := url.Values{ 735 | "chatId": {message.Chat.ID}, 736 | "caption": {message.Text}, 737 | "fileId": {message.FileID}, 738 | } 739 | 740 | if message.ReplyMsgID != "" { 741 | params.Set("replyMsgId", message.ReplyMsgID) 742 | } 743 | 744 | if message.ForwardMsgID != "" { 745 | params.Set("forwardMsgId", message.ForwardMsgID) 746 | params.Set("forwardChatId", message.ForwardChatID) 747 | } 748 | 749 | if message.InlineKeyboard != nil { 750 | data, err := json.Marshal(message.InlineKeyboard.GetKeyboard()) 751 | if err != nil { 752 | return fmt.Errorf("cannot marshal inline keyboard markup: %s", err) 753 | } 754 | 755 | params.Set("inlineKeyboardMarkup", string(data)) 756 | } 757 | 758 | if message.ParseMode != "" { 759 | params.Set("parseMode", string(message.ParseMode)) 760 | } 761 | 762 | response, err := c.Do("/messages/sendFile", params, nil) 763 | if err != nil { 764 | return fmt.Errorf("error while making request: %s", err) 765 | } 766 | 767 | if err := json.Unmarshal(response, message); err != nil { 768 | return fmt.Errorf("cannot unmarshal response: %s", err) 769 | } 770 | 771 | return nil 772 | } 773 | 774 | func (c *Client) SendVoiceMessage(message *Message) error { 775 | if message == nil { 776 | return fmt.Errorf("message cannot be nil") 777 | } 778 | if message.Chat.ID == "" { 779 | return fmt.Errorf("chatID cannot be empty") 780 | } 781 | if message.FileID == "" { 782 | return fmt.Errorf("fileID cannot be empty") 783 | } 784 | 785 | params := url.Values{ 786 | "chatId": {message.Chat.ID}, 787 | "caption": {message.Text}, 788 | "fileId": {message.FileID}, 789 | } 790 | 791 | if message.ReplyMsgID != "" { 792 | params.Set("replyMsgId", message.ReplyMsgID) 793 | } 794 | 795 | if message.ForwardMsgID != "" { 796 | params.Set("forwardMsgId", message.ForwardMsgID) 797 | params.Set("forwardChatId", message.ForwardChatID) 798 | } 799 | 800 | if message.InlineKeyboard != nil { 801 | data, err := json.Marshal(message.InlineKeyboard.GetKeyboard()) 802 | if err != nil { 803 | return fmt.Errorf("cannot marshal inline keyboard markup: %s", err) 804 | } 805 | 806 | params.Set("inlineKeyboardMarkup", string(data)) 807 | } 808 | 809 | response, err := c.Do("/messages/sendVoice", params, nil) 810 | if err != nil { 811 | return fmt.Errorf("error while making request: %s", err) 812 | } 813 | 814 | if err := json.Unmarshal(response, message); err != nil { 815 | return fmt.Errorf("cannot unmarshal response: %s", err) 816 | } 817 | 818 | return nil 819 | } 820 | 821 | func (c *Client) UploadFile(message *Message) error { 822 | if message == nil { 823 | return fmt.Errorf("message cannot be nil") 824 | } 825 | if message.Chat.ID == "" { 826 | return fmt.Errorf("chatID cannot be empty") 827 | } 828 | if message.File == nil { 829 | return fmt.Errorf("file cannot be nil") 830 | } 831 | 832 | params := url.Values{ 833 | "chatId": {message.Chat.ID}, 834 | "caption": {message.Text}, 835 | } 836 | 837 | if message.InlineKeyboard != nil { 838 | data, err := json.Marshal(message.InlineKeyboard.GetKeyboard()) 839 | if err != nil { 840 | return fmt.Errorf("cannot marshal inline keyboard markup: %s", err) 841 | } 842 | 843 | params.Set("inlineKeyboardMarkup", string(data)) 844 | } 845 | 846 | response, err := c.Do("/messages/sendFile", params, message.File) 847 | if err != nil { 848 | return fmt.Errorf("error while making request: %s", err) 849 | } 850 | 851 | if err := json.Unmarshal(response, message); err != nil { 852 | return fmt.Errorf("cannot unmarshal response: %s", err) 853 | } 854 | 855 | return nil 856 | } 857 | 858 | func (c *Client) UploadVoice(message *Message) error { 859 | if message == nil { 860 | return fmt.Errorf("message cannot be nil") 861 | } 862 | if message.Chat.ID == "" { 863 | return fmt.Errorf("chatID cannot be empty") 864 | } 865 | if message.File == nil { 866 | return fmt.Errorf("file cannot be nil") 867 | } 868 | 869 | params := url.Values{ 870 | "chatId": {message.Chat.ID}, 871 | "caption": {message.Text}, 872 | } 873 | 874 | if message.InlineKeyboard != nil { 875 | data, err := json.Marshal(message.InlineKeyboard.GetKeyboard()) 876 | if err != nil { 877 | return fmt.Errorf("cannot marshal inline keyboard markup: %s", err) 878 | } 879 | 880 | params.Set("inlineKeyboardMarkup", string(data)) 881 | } 882 | 883 | response, err := c.Do("/messages/sendVoice", params, message.File) 884 | if err != nil { 885 | return fmt.Errorf("error while making request: %s", err) 886 | } 887 | 888 | if err := json.Unmarshal(response, message); err != nil { 889 | return fmt.Errorf("cannot unmarshal response: %s", err) 890 | } 891 | 892 | return nil 893 | } 894 | 895 | func (c *Client) GetEvents(lastEventID int, pollTime int) ([]*Event, error) { 896 | return c.GetEventsWithContext(context.Background(), lastEventID, pollTime) 897 | } 898 | 899 | func (c *Client) GetEventsWithContext(ctx context.Context, lastEventID int, pollTime int) ([]*Event, error) { 900 | params := url.Values{ 901 | "lastEventId": {strconv.Itoa(lastEventID)}, 902 | "pollTime": {strconv.Itoa(pollTime)}, 903 | } 904 | events := &eventsResponse{} 905 | 906 | response, err := c.DoWithContext(ctx, "/events/get", params, nil) 907 | if err != nil { 908 | return events.Events, fmt.Errorf("error while making request: %s", err) 909 | } 910 | 911 | if err := json.Unmarshal(response, events); err != nil { 912 | return events.Events, fmt.Errorf("cannot parse events: %s", err) 913 | } 914 | 915 | return events.Events, nil 916 | } 917 | 918 | func (c *Client) PinMessage(message *Message) error { 919 | if message == nil { 920 | return fmt.Errorf("message cannot be nil") 921 | } 922 | if message.Chat.ID == "" { 923 | return fmt.Errorf("chatID cannot be empty") 924 | } 925 | if message.ID == "" { 926 | return fmt.Errorf("message ID cannot be empty") 927 | } 928 | 929 | params := url.Values{ 930 | "chatId": {message.Chat.ID}, 931 | "msgId": {message.ID}, 932 | } 933 | _, err := c.Do("/chats/pinMessage", params, nil) 934 | if err != nil { 935 | return fmt.Errorf("error while pinning message: %s", err) 936 | } 937 | 938 | return nil 939 | } 940 | 941 | func (c *Client) UnpinMessage(message *Message) error { 942 | if message == nil { 943 | return fmt.Errorf("message cannot be nil") 944 | } 945 | if message.Chat.ID == "" { 946 | return fmt.Errorf("chatID cannot be empty") 947 | } 948 | if message.ID == "" { 949 | return fmt.Errorf("message ID cannot be empty") 950 | } 951 | 952 | params := url.Values{ 953 | "chatId": {message.Chat.ID}, 954 | "msgId": {message.ID}, 955 | } 956 | _, err := c.Do("/chats/unpinMessage", params, nil) 957 | if err != nil { 958 | return fmt.Errorf("error while unpinning message: %s", err) 959 | } 960 | 961 | return nil 962 | } 963 | 964 | func (c *Client) SendAnswerCallbackQuery(answer *ButtonResponse) error { 965 | if answer == nil { 966 | return fmt.Errorf("answer cannot be nil") 967 | } 968 | if answer.QueryID == "" { 969 | return fmt.Errorf("queryID cannot be empty") 970 | } 971 | 972 | params := url.Values{ 973 | "queryId": {answer.QueryID}, 974 | "text": {answer.Text}, 975 | "url": {answer.URL}, 976 | "showAlert": {strconv.FormatBool(answer.ShowAlert)}, 977 | } 978 | 979 | _, err := c.Do("/messages/answerCallbackQuery", params, nil) 980 | if err != nil { 981 | return fmt.Errorf("error while making request: %s", err) 982 | } 983 | 984 | return nil 985 | } 986 | 987 | func NewClient(baseURL string, token string, logger *logrus.Logger) *Client { 988 | return NewCustomClient(http.DefaultClient, baseURL, token, logger) 989 | } 990 | 991 | func NewCustomClient(client *http.Client, baseURL string, token string, logger *logrus.Logger) *Client { 992 | return &Client{ 993 | token: token, 994 | baseURL: baseURL, 995 | client: client, 996 | logger: logger, 997 | } 998 | } 999 | -------------------------------------------------------------------------------- /types_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package botgolang 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang(in *jlexer.Lexer, out *eventsResponse) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeFieldName(false) 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "ok": 40 | out.OK = bool(in.Bool()) 41 | case "events": 42 | if in.IsNull() { 43 | in.Skip() 44 | out.Events = nil 45 | } else { 46 | in.Delim('[') 47 | if out.Events == nil { 48 | if !in.IsDelim(']') { 49 | out.Events = make([]*Event, 0, 8) 50 | } else { 51 | out.Events = []*Event{} 52 | } 53 | } else { 54 | out.Events = (out.Events)[:0] 55 | } 56 | for !in.IsDelim(']') { 57 | var v1 *Event 58 | if in.IsNull() { 59 | in.Skip() 60 | v1 = nil 61 | } else { 62 | if v1 == nil { 63 | v1 = new(Event) 64 | } 65 | (*v1).UnmarshalEasyJSON(in) 66 | } 67 | out.Events = append(out.Events, v1) 68 | in.WantComma() 69 | } 70 | in.Delim(']') 71 | } 72 | default: 73 | in.SkipRecursive() 74 | } 75 | in.WantComma() 76 | } 77 | in.Delim('}') 78 | if isTopLevel { 79 | in.Consumed() 80 | } 81 | } 82 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang(out *jwriter.Writer, in eventsResponse) { 83 | out.RawByte('{') 84 | first := true 85 | _ = first 86 | { 87 | const prefix string = ",\"ok\":" 88 | out.RawString(prefix[1:]) 89 | out.Bool(bool(in.OK)) 90 | } 91 | { 92 | const prefix string = ",\"events\":" 93 | out.RawString(prefix) 94 | if in.Events == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 95 | out.RawString("null") 96 | } else { 97 | out.RawByte('[') 98 | for v2, v3 := range in.Events { 99 | if v2 > 0 { 100 | out.RawByte(',') 101 | } 102 | if v3 == nil { 103 | out.RawString("null") 104 | } else { 105 | (*v3).MarshalEasyJSON(out) 106 | } 107 | } 108 | out.RawByte(']') 109 | } 110 | } 111 | out.RawByte('}') 112 | } 113 | 114 | // MarshalJSON supports json.Marshaler interface 115 | func (v eventsResponse) MarshalJSON() ([]byte, error) { 116 | w := jwriter.Writer{} 117 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang(&w, v) 118 | return w.Buffer.BuildBytes(), w.Error 119 | } 120 | 121 | // MarshalEasyJSON supports easyjson.Marshaler interface 122 | func (v eventsResponse) MarshalEasyJSON(w *jwriter.Writer) { 123 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang(w, v) 124 | } 125 | 126 | // UnmarshalJSON supports json.Unmarshaler interface 127 | func (v *eventsResponse) UnmarshalJSON(data []byte) error { 128 | r := jlexer.Lexer{Data: data} 129 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang(&r, v) 130 | return r.Error() 131 | } 132 | 133 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 134 | func (v *eventsResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { 135 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang(l, v) 136 | } 137 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang1(in *jlexer.Lexer, out *UsersListResponse) { 138 | isTopLevel := in.IsStart() 139 | if in.IsNull() { 140 | if isTopLevel { 141 | in.Consumed() 142 | } 143 | in.Skip() 144 | return 145 | } 146 | in.Delim('{') 147 | for !in.IsDelim('}') { 148 | key := in.UnsafeFieldName(false) 149 | in.WantColon() 150 | if in.IsNull() { 151 | in.Skip() 152 | in.WantComma() 153 | continue 154 | } 155 | switch key { 156 | case "users": 157 | if in.IsNull() { 158 | in.Skip() 159 | out.List = nil 160 | } else { 161 | in.Delim('[') 162 | if out.List == nil { 163 | if !in.IsDelim(']') { 164 | out.List = make([]User, 0, 4) 165 | } else { 166 | out.List = []User{} 167 | } 168 | } else { 169 | out.List = (out.List)[:0] 170 | } 171 | for !in.IsDelim(']') { 172 | var v4 User 173 | (v4).UnmarshalEasyJSON(in) 174 | out.List = append(out.List, v4) 175 | in.WantComma() 176 | } 177 | in.Delim(']') 178 | } 179 | default: 180 | in.SkipRecursive() 181 | } 182 | in.WantComma() 183 | } 184 | in.Delim('}') 185 | if isTopLevel { 186 | in.Consumed() 187 | } 188 | } 189 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang1(out *jwriter.Writer, in UsersListResponse) { 190 | out.RawByte('{') 191 | first := true 192 | _ = first 193 | { 194 | const prefix string = ",\"users\":" 195 | out.RawString(prefix[1:]) 196 | if in.List == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 197 | out.RawString("null") 198 | } else { 199 | out.RawByte('[') 200 | for v5, v6 := range in.List { 201 | if v5 > 0 { 202 | out.RawByte(',') 203 | } 204 | (v6).MarshalEasyJSON(out) 205 | } 206 | out.RawByte(']') 207 | } 208 | } 209 | out.RawByte('}') 210 | } 211 | 212 | // MarshalJSON supports json.Marshaler interface 213 | func (v UsersListResponse) MarshalJSON() ([]byte, error) { 214 | w := jwriter.Writer{} 215 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang1(&w, v) 216 | return w.Buffer.BuildBytes(), w.Error 217 | } 218 | 219 | // MarshalEasyJSON supports easyjson.Marshaler interface 220 | func (v UsersListResponse) MarshalEasyJSON(w *jwriter.Writer) { 221 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang1(w, v) 222 | } 223 | 224 | // UnmarshalJSON supports json.Unmarshaler interface 225 | func (v *UsersListResponse) UnmarshalJSON(data []byte) error { 226 | r := jlexer.Lexer{Data: data} 227 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang1(&r, v) 228 | return r.Error() 229 | } 230 | 231 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 232 | func (v *UsersListResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { 233 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang1(l, v) 234 | } 235 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang2(in *jlexer.Lexer, out *UserState) { 236 | isTopLevel := in.IsStart() 237 | if in.IsNull() { 238 | if isTopLevel { 239 | in.Consumed() 240 | } 241 | in.Skip() 242 | return 243 | } 244 | in.Delim('{') 245 | for !in.IsDelim('}') { 246 | key := in.UnsafeFieldName(false) 247 | in.WantColon() 248 | if in.IsNull() { 249 | in.Skip() 250 | in.WantComma() 251 | continue 252 | } 253 | switch key { 254 | case "lastseen": 255 | out.Lastseen = int(in.Int()) 256 | default: 257 | in.SkipRecursive() 258 | } 259 | in.WantComma() 260 | } 261 | in.Delim('}') 262 | if isTopLevel { 263 | in.Consumed() 264 | } 265 | } 266 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang2(out *jwriter.Writer, in UserState) { 267 | out.RawByte('{') 268 | first := true 269 | _ = first 270 | { 271 | const prefix string = ",\"lastseen\":" 272 | out.RawString(prefix[1:]) 273 | out.Int(int(in.Lastseen)) 274 | } 275 | out.RawByte('}') 276 | } 277 | 278 | // MarshalJSON supports json.Marshaler interface 279 | func (v UserState) MarshalJSON() ([]byte, error) { 280 | w := jwriter.Writer{} 281 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang2(&w, v) 282 | return w.Buffer.BuildBytes(), w.Error 283 | } 284 | 285 | // MarshalEasyJSON supports easyjson.Marshaler interface 286 | func (v UserState) MarshalEasyJSON(w *jwriter.Writer) { 287 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang2(w, v) 288 | } 289 | 290 | // UnmarshalJSON supports json.Unmarshaler interface 291 | func (v *UserState) UnmarshalJSON(data []byte) error { 292 | r := jlexer.Lexer{Data: data} 293 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang2(&r, v) 294 | return r.Error() 295 | } 296 | 297 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 298 | func (v *UserState) UnmarshalEasyJSON(l *jlexer.Lexer) { 299 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang2(l, v) 300 | } 301 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang3(in *jlexer.Lexer, out *User) { 302 | isTopLevel := in.IsStart() 303 | if in.IsNull() { 304 | if isTopLevel { 305 | in.Consumed() 306 | } 307 | in.Skip() 308 | return 309 | } 310 | in.Delim('{') 311 | for !in.IsDelim('}') { 312 | key := in.UnsafeFieldName(false) 313 | in.WantColon() 314 | if in.IsNull() { 315 | in.Skip() 316 | in.WantComma() 317 | continue 318 | } 319 | switch key { 320 | case "userId": 321 | out.ID = string(in.String()) 322 | default: 323 | in.SkipRecursive() 324 | } 325 | in.WantComma() 326 | } 327 | in.Delim('}') 328 | if isTopLevel { 329 | in.Consumed() 330 | } 331 | } 332 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang3(out *jwriter.Writer, in User) { 333 | out.RawByte('{') 334 | first := true 335 | _ = first 336 | { 337 | const prefix string = ",\"userId\":" 338 | out.RawString(prefix[1:]) 339 | out.String(string(in.ID)) 340 | } 341 | out.RawByte('}') 342 | } 343 | 344 | // MarshalJSON supports json.Marshaler interface 345 | func (v User) MarshalJSON() ([]byte, error) { 346 | w := jwriter.Writer{} 347 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang3(&w, v) 348 | return w.Buffer.BuildBytes(), w.Error 349 | } 350 | 351 | // MarshalEasyJSON supports easyjson.Marshaler interface 352 | func (v User) MarshalEasyJSON(w *jwriter.Writer) { 353 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang3(w, v) 354 | } 355 | 356 | // UnmarshalJSON supports json.Unmarshaler interface 357 | func (v *User) UnmarshalJSON(data []byte) error { 358 | r := jlexer.Lexer{Data: data} 359 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang3(&r, v) 360 | return r.Error() 361 | } 362 | 363 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 364 | func (v *User) UnmarshalEasyJSON(l *jlexer.Lexer) { 365 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang3(l, v) 366 | } 367 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang4(in *jlexer.Lexer, out *ThreadSubscribers) { 368 | isTopLevel := in.IsStart() 369 | if in.IsNull() { 370 | if isTopLevel { 371 | in.Consumed() 372 | } 373 | in.Skip() 374 | return 375 | } 376 | in.Delim('{') 377 | for !in.IsDelim('}') { 378 | key := in.UnsafeFieldName(false) 379 | in.WantColon() 380 | if in.IsNull() { 381 | in.Skip() 382 | in.WantComma() 383 | continue 384 | } 385 | switch key { 386 | case "cursor": 387 | out.Cursor = string(in.String()) 388 | case "subscribers": 389 | if in.IsNull() { 390 | in.Skip() 391 | out.Subscribers = nil 392 | } else { 393 | in.Delim('[') 394 | if out.Subscribers == nil { 395 | if !in.IsDelim(']') { 396 | out.Subscribers = make([]Subscriber, 0, 2) 397 | } else { 398 | out.Subscribers = []Subscriber{} 399 | } 400 | } else { 401 | out.Subscribers = (out.Subscribers)[:0] 402 | } 403 | for !in.IsDelim(']') { 404 | var v7 Subscriber 405 | (v7).UnmarshalEasyJSON(in) 406 | out.Subscribers = append(out.Subscribers, v7) 407 | in.WantComma() 408 | } 409 | in.Delim(']') 410 | } 411 | default: 412 | in.SkipRecursive() 413 | } 414 | in.WantComma() 415 | } 416 | in.Delim('}') 417 | if isTopLevel { 418 | in.Consumed() 419 | } 420 | } 421 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang4(out *jwriter.Writer, in ThreadSubscribers) { 422 | out.RawByte('{') 423 | first := true 424 | _ = first 425 | { 426 | const prefix string = ",\"cursor\":" 427 | out.RawString(prefix[1:]) 428 | out.String(string(in.Cursor)) 429 | } 430 | { 431 | const prefix string = ",\"subscribers\":" 432 | out.RawString(prefix) 433 | if in.Subscribers == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 434 | out.RawString("null") 435 | } else { 436 | out.RawByte('[') 437 | for v8, v9 := range in.Subscribers { 438 | if v8 > 0 { 439 | out.RawByte(',') 440 | } 441 | (v9).MarshalEasyJSON(out) 442 | } 443 | out.RawByte(']') 444 | } 445 | } 446 | out.RawByte('}') 447 | } 448 | 449 | // MarshalJSON supports json.Marshaler interface 450 | func (v ThreadSubscribers) MarshalJSON() ([]byte, error) { 451 | w := jwriter.Writer{} 452 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang4(&w, v) 453 | return w.Buffer.BuildBytes(), w.Error 454 | } 455 | 456 | // MarshalEasyJSON supports easyjson.Marshaler interface 457 | func (v ThreadSubscribers) MarshalEasyJSON(w *jwriter.Writer) { 458 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang4(w, v) 459 | } 460 | 461 | // UnmarshalJSON supports json.Unmarshaler interface 462 | func (v *ThreadSubscribers) UnmarshalJSON(data []byte) error { 463 | r := jlexer.Lexer{Data: data} 464 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang4(&r, v) 465 | return r.Error() 466 | } 467 | 468 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 469 | func (v *ThreadSubscribers) UnmarshalEasyJSON(l *jlexer.Lexer) { 470 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang4(l, v) 471 | } 472 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang5(in *jlexer.Lexer, out *Thread) { 473 | isTopLevel := in.IsStart() 474 | if in.IsNull() { 475 | if isTopLevel { 476 | in.Consumed() 477 | } 478 | in.Skip() 479 | return 480 | } 481 | in.Delim('{') 482 | for !in.IsDelim('}') { 483 | key := in.UnsafeFieldName(false) 484 | in.WantColon() 485 | if in.IsNull() { 486 | in.Skip() 487 | in.WantComma() 488 | continue 489 | } 490 | switch key { 491 | case "threadId": 492 | out.ThreadID = string(in.String()) 493 | default: 494 | in.SkipRecursive() 495 | } 496 | in.WantComma() 497 | } 498 | in.Delim('}') 499 | if isTopLevel { 500 | in.Consumed() 501 | } 502 | } 503 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang5(out *jwriter.Writer, in Thread) { 504 | out.RawByte('{') 505 | first := true 506 | _ = first 507 | { 508 | const prefix string = ",\"threadId\":" 509 | out.RawString(prefix[1:]) 510 | out.String(string(in.ThreadID)) 511 | } 512 | out.RawByte('}') 513 | } 514 | 515 | // MarshalJSON supports json.Marshaler interface 516 | func (v Thread) MarshalJSON() ([]byte, error) { 517 | w := jwriter.Writer{} 518 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang5(&w, v) 519 | return w.Buffer.BuildBytes(), w.Error 520 | } 521 | 522 | // MarshalEasyJSON supports easyjson.Marshaler interface 523 | func (v Thread) MarshalEasyJSON(w *jwriter.Writer) { 524 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang5(w, v) 525 | } 526 | 527 | // UnmarshalJSON supports json.Unmarshaler interface 528 | func (v *Thread) UnmarshalJSON(data []byte) error { 529 | r := jlexer.Lexer{Data: data} 530 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang5(&r, v) 531 | return r.Error() 532 | } 533 | 534 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 535 | func (v *Thread) UnmarshalEasyJSON(l *jlexer.Lexer) { 536 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang5(l, v) 537 | } 538 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang6(in *jlexer.Lexer, out *Subscriber) { 539 | isTopLevel := in.IsStart() 540 | if in.IsNull() { 541 | if isTopLevel { 542 | in.Consumed() 543 | } 544 | in.Skip() 545 | return 546 | } 547 | in.Delim('{') 548 | for !in.IsDelim('}') { 549 | key := in.UnsafeFieldName(false) 550 | in.WantColon() 551 | if in.IsNull() { 552 | in.Skip() 553 | in.WantComma() 554 | continue 555 | } 556 | switch key { 557 | case "sn": 558 | out.SN = string(in.String()) 559 | case "userState": 560 | (out.UserState).UnmarshalEasyJSON(in) 561 | default: 562 | in.SkipRecursive() 563 | } 564 | in.WantComma() 565 | } 566 | in.Delim('}') 567 | if isTopLevel { 568 | in.Consumed() 569 | } 570 | } 571 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang6(out *jwriter.Writer, in Subscriber) { 572 | out.RawByte('{') 573 | first := true 574 | _ = first 575 | { 576 | const prefix string = ",\"sn\":" 577 | out.RawString(prefix[1:]) 578 | out.String(string(in.SN)) 579 | } 580 | { 581 | const prefix string = ",\"userState\":" 582 | out.RawString(prefix) 583 | (in.UserState).MarshalEasyJSON(out) 584 | } 585 | out.RawByte('}') 586 | } 587 | 588 | // MarshalJSON supports json.Marshaler interface 589 | func (v Subscriber) MarshalJSON() ([]byte, error) { 590 | w := jwriter.Writer{} 591 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang6(&w, v) 592 | return w.Buffer.BuildBytes(), w.Error 593 | } 594 | 595 | // MarshalEasyJSON supports easyjson.Marshaler interface 596 | func (v Subscriber) MarshalEasyJSON(w *jwriter.Writer) { 597 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang6(w, v) 598 | } 599 | 600 | // UnmarshalJSON supports json.Unmarshaler interface 601 | func (v *Subscriber) UnmarshalJSON(data []byte) error { 602 | r := jlexer.Lexer{Data: data} 603 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang6(&r, v) 604 | return r.Error() 605 | } 606 | 607 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 608 | func (v *Subscriber) UnmarshalEasyJSON(l *jlexer.Lexer) { 609 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang6(l, v) 610 | } 611 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang7(in *jlexer.Lexer, out *Response) { 612 | isTopLevel := in.IsStart() 613 | if in.IsNull() { 614 | if isTopLevel { 615 | in.Consumed() 616 | } 617 | in.Skip() 618 | return 619 | } 620 | in.Delim('{') 621 | for !in.IsDelim('}') { 622 | key := in.UnsafeFieldName(false) 623 | in.WantColon() 624 | if in.IsNull() { 625 | in.Skip() 626 | in.WantComma() 627 | continue 628 | } 629 | switch key { 630 | case "ok": 631 | out.OK = bool(in.Bool()) 632 | case "description": 633 | out.Description = string(in.String()) 634 | default: 635 | in.SkipRecursive() 636 | } 637 | in.WantComma() 638 | } 639 | in.Delim('}') 640 | if isTopLevel { 641 | in.Consumed() 642 | } 643 | } 644 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang7(out *jwriter.Writer, in Response) { 645 | out.RawByte('{') 646 | first := true 647 | _ = first 648 | { 649 | const prefix string = ",\"ok\":" 650 | out.RawString(prefix[1:]) 651 | out.Bool(bool(in.OK)) 652 | } 653 | if in.Description != "" { 654 | const prefix string = ",\"description\":" 655 | out.RawString(prefix) 656 | out.String(string(in.Description)) 657 | } 658 | out.RawByte('}') 659 | } 660 | 661 | // MarshalJSON supports json.Marshaler interface 662 | func (v Response) MarshalJSON() ([]byte, error) { 663 | w := jwriter.Writer{} 664 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang7(&w, v) 665 | return w.Buffer.BuildBytes(), w.Error 666 | } 667 | 668 | // MarshalEasyJSON supports easyjson.Marshaler interface 669 | func (v Response) MarshalEasyJSON(w *jwriter.Writer) { 670 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang7(w, v) 671 | } 672 | 673 | // UnmarshalJSON supports json.Unmarshaler interface 674 | func (v *Response) UnmarshalJSON(data []byte) error { 675 | r := jlexer.Lexer{Data: data} 676 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang7(&r, v) 677 | return r.Error() 678 | } 679 | 680 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 681 | func (v *Response) UnmarshalEasyJSON(l *jlexer.Lexer) { 682 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang7(l, v) 683 | } 684 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang8(in *jlexer.Lexer, out *Photo) { 685 | isTopLevel := in.IsStart() 686 | if in.IsNull() { 687 | if isTopLevel { 688 | in.Consumed() 689 | } 690 | in.Skip() 691 | return 692 | } 693 | in.Delim('{') 694 | for !in.IsDelim('}') { 695 | key := in.UnsafeFieldName(false) 696 | in.WantColon() 697 | if in.IsNull() { 698 | in.Skip() 699 | in.WantComma() 700 | continue 701 | } 702 | switch key { 703 | case "url": 704 | out.URL = string(in.String()) 705 | default: 706 | in.SkipRecursive() 707 | } 708 | in.WantComma() 709 | } 710 | in.Delim('}') 711 | if isTopLevel { 712 | in.Consumed() 713 | } 714 | } 715 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang8(out *jwriter.Writer, in Photo) { 716 | out.RawByte('{') 717 | first := true 718 | _ = first 719 | { 720 | const prefix string = ",\"url\":" 721 | out.RawString(prefix[1:]) 722 | out.String(string(in.URL)) 723 | } 724 | out.RawByte('}') 725 | } 726 | 727 | // MarshalJSON supports json.Marshaler interface 728 | func (v Photo) MarshalJSON() ([]byte, error) { 729 | w := jwriter.Writer{} 730 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang8(&w, v) 731 | return w.Buffer.BuildBytes(), w.Error 732 | } 733 | 734 | // MarshalEasyJSON supports easyjson.Marshaler interface 735 | func (v Photo) MarshalEasyJSON(w *jwriter.Writer) { 736 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang8(w, v) 737 | } 738 | 739 | // UnmarshalJSON supports json.Unmarshaler interface 740 | func (v *Photo) UnmarshalJSON(data []byte) error { 741 | r := jlexer.Lexer{Data: data} 742 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang8(&r, v) 743 | return r.Error() 744 | } 745 | 746 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 747 | func (v *Photo) UnmarshalEasyJSON(l *jlexer.Lexer) { 748 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang8(l, v) 749 | } 750 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang9(in *jlexer.Lexer, out *PartPayload) { 751 | isTopLevel := in.IsStart() 752 | if in.IsNull() { 753 | if isTopLevel { 754 | in.Consumed() 755 | } 756 | in.Skip() 757 | return 758 | } 759 | in.Delim('{') 760 | for !in.IsDelim('}') { 761 | key := in.UnsafeFieldName(false) 762 | in.WantColon() 763 | if in.IsNull() { 764 | in.Skip() 765 | in.WantComma() 766 | continue 767 | } 768 | switch key { 769 | case "firstName": 770 | out.FirstName = string(in.String()) 771 | case "lastName": 772 | out.LastName = string(in.String()) 773 | case "userId": 774 | out.UserID = string(in.String()) 775 | case "fileId": 776 | out.FileID = string(in.String()) 777 | case "caption": 778 | out.Caption = string(in.String()) 779 | case "type": 780 | out.Type = string(in.String()) 781 | case "message": 782 | (out.PartMessage).UnmarshalEasyJSON(in) 783 | default: 784 | in.SkipRecursive() 785 | } 786 | in.WantComma() 787 | } 788 | in.Delim('}') 789 | if isTopLevel { 790 | in.Consumed() 791 | } 792 | } 793 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang9(out *jwriter.Writer, in PartPayload) { 794 | out.RawByte('{') 795 | first := true 796 | _ = first 797 | { 798 | const prefix string = ",\"firstName\":" 799 | out.RawString(prefix[1:]) 800 | out.String(string(in.FirstName)) 801 | } 802 | { 803 | const prefix string = ",\"lastName\":" 804 | out.RawString(prefix) 805 | out.String(string(in.LastName)) 806 | } 807 | { 808 | const prefix string = ",\"userId\":" 809 | out.RawString(prefix) 810 | out.String(string(in.UserID)) 811 | } 812 | { 813 | const prefix string = ",\"fileId\":" 814 | out.RawString(prefix) 815 | out.String(string(in.FileID)) 816 | } 817 | { 818 | const prefix string = ",\"caption\":" 819 | out.RawString(prefix) 820 | out.String(string(in.Caption)) 821 | } 822 | { 823 | const prefix string = ",\"type\":" 824 | out.RawString(prefix) 825 | out.String(string(in.Type)) 826 | } 827 | { 828 | const prefix string = ",\"message\":" 829 | out.RawString(prefix) 830 | (in.PartMessage).MarshalEasyJSON(out) 831 | } 832 | out.RawByte('}') 833 | } 834 | 835 | // MarshalJSON supports json.Marshaler interface 836 | func (v PartPayload) MarshalJSON() ([]byte, error) { 837 | w := jwriter.Writer{} 838 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang9(&w, v) 839 | return w.Buffer.BuildBytes(), w.Error 840 | } 841 | 842 | // MarshalEasyJSON supports easyjson.Marshaler interface 843 | func (v PartPayload) MarshalEasyJSON(w *jwriter.Writer) { 844 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang9(w, v) 845 | } 846 | 847 | // UnmarshalJSON supports json.Unmarshaler interface 848 | func (v *PartPayload) UnmarshalJSON(data []byte) error { 849 | r := jlexer.Lexer{Data: data} 850 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang9(&r, v) 851 | return r.Error() 852 | } 853 | 854 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 855 | func (v *PartPayload) UnmarshalEasyJSON(l *jlexer.Lexer) { 856 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang9(l, v) 857 | } 858 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang10(in *jlexer.Lexer, out *PartMessage) { 859 | isTopLevel := in.IsStart() 860 | if in.IsNull() { 861 | if isTopLevel { 862 | in.Consumed() 863 | } 864 | in.Skip() 865 | return 866 | } 867 | in.Delim('{') 868 | for !in.IsDelim('}') { 869 | key := in.UnsafeFieldName(false) 870 | in.WantColon() 871 | if in.IsNull() { 872 | in.Skip() 873 | in.WantComma() 874 | continue 875 | } 876 | switch key { 877 | case "from": 878 | (out.From).UnmarshalEasyJSON(in) 879 | case "msgId": 880 | out.MsgID = string(in.String()) 881 | case "text": 882 | out.Text = string(in.String()) 883 | case "timestamp": 884 | out.Timestamp = int(in.Int()) 885 | default: 886 | in.SkipRecursive() 887 | } 888 | in.WantComma() 889 | } 890 | in.Delim('}') 891 | if isTopLevel { 892 | in.Consumed() 893 | } 894 | } 895 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang10(out *jwriter.Writer, in PartMessage) { 896 | out.RawByte('{') 897 | first := true 898 | _ = first 899 | { 900 | const prefix string = ",\"from\":" 901 | out.RawString(prefix[1:]) 902 | (in.From).MarshalEasyJSON(out) 903 | } 904 | { 905 | const prefix string = ",\"msgId\":" 906 | out.RawString(prefix) 907 | out.String(string(in.MsgID)) 908 | } 909 | { 910 | const prefix string = ",\"text\":" 911 | out.RawString(prefix) 912 | out.String(string(in.Text)) 913 | } 914 | { 915 | const prefix string = ",\"timestamp\":" 916 | out.RawString(prefix) 917 | out.Int(int(in.Timestamp)) 918 | } 919 | out.RawByte('}') 920 | } 921 | 922 | // MarshalJSON supports json.Marshaler interface 923 | func (v PartMessage) MarshalJSON() ([]byte, error) { 924 | w := jwriter.Writer{} 925 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang10(&w, v) 926 | return w.Buffer.BuildBytes(), w.Error 927 | } 928 | 929 | // MarshalEasyJSON supports easyjson.Marshaler interface 930 | func (v PartMessage) MarshalEasyJSON(w *jwriter.Writer) { 931 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang10(w, v) 932 | } 933 | 934 | // UnmarshalJSON supports json.Unmarshaler interface 935 | func (v *PartMessage) UnmarshalJSON(data []byte) error { 936 | r := jlexer.Lexer{Data: data} 937 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang10(&r, v) 938 | return r.Error() 939 | } 940 | 941 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 942 | func (v *PartMessage) UnmarshalEasyJSON(l *jlexer.Lexer) { 943 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang10(l, v) 944 | } 945 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang11(in *jlexer.Lexer, out *Part) { 946 | isTopLevel := in.IsStart() 947 | if in.IsNull() { 948 | if isTopLevel { 949 | in.Consumed() 950 | } 951 | in.Skip() 952 | return 953 | } 954 | in.Delim('{') 955 | for !in.IsDelim('}') { 956 | key := in.UnsafeFieldName(false) 957 | in.WantColon() 958 | if in.IsNull() { 959 | in.Skip() 960 | in.WantComma() 961 | continue 962 | } 963 | switch key { 964 | case "type": 965 | out.Type = PartType(in.String()) 966 | case "payload": 967 | (out.Payload).UnmarshalEasyJSON(in) 968 | default: 969 | in.SkipRecursive() 970 | } 971 | in.WantComma() 972 | } 973 | in.Delim('}') 974 | if isTopLevel { 975 | in.Consumed() 976 | } 977 | } 978 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang11(out *jwriter.Writer, in Part) { 979 | out.RawByte('{') 980 | first := true 981 | _ = first 982 | { 983 | const prefix string = ",\"type\":" 984 | out.RawString(prefix[1:]) 985 | out.String(string(in.Type)) 986 | } 987 | { 988 | const prefix string = ",\"payload\":" 989 | out.RawString(prefix) 990 | (in.Payload).MarshalEasyJSON(out) 991 | } 992 | out.RawByte('}') 993 | } 994 | 995 | // MarshalJSON supports json.Marshaler interface 996 | func (v Part) MarshalJSON() ([]byte, error) { 997 | w := jwriter.Writer{} 998 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang11(&w, v) 999 | return w.Buffer.BuildBytes(), w.Error 1000 | } 1001 | 1002 | // MarshalEasyJSON supports easyjson.Marshaler interface 1003 | func (v Part) MarshalEasyJSON(w *jwriter.Writer) { 1004 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang11(w, v) 1005 | } 1006 | 1007 | // UnmarshalJSON supports json.Unmarshaler interface 1008 | func (v *Part) UnmarshalJSON(data []byte) error { 1009 | r := jlexer.Lexer{Data: data} 1010 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang11(&r, v) 1011 | return r.Error() 1012 | } 1013 | 1014 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1015 | func (v *Part) UnmarshalEasyJSON(l *jlexer.Lexer) { 1016 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang11(l, v) 1017 | } 1018 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang12(in *jlexer.Lexer, out *MembersListResponse) { 1019 | isTopLevel := in.IsStart() 1020 | if in.IsNull() { 1021 | if isTopLevel { 1022 | in.Consumed() 1023 | } 1024 | in.Skip() 1025 | return 1026 | } 1027 | in.Delim('{') 1028 | for !in.IsDelim('}') { 1029 | key := in.UnsafeFieldName(false) 1030 | in.WantColon() 1031 | if in.IsNull() { 1032 | in.Skip() 1033 | in.WantComma() 1034 | continue 1035 | } 1036 | switch key { 1037 | case "members": 1038 | if in.IsNull() { 1039 | in.Skip() 1040 | out.List = nil 1041 | } else { 1042 | in.Delim('[') 1043 | if out.List == nil { 1044 | if !in.IsDelim(']') { 1045 | out.List = make([]ChatMember, 0, 2) 1046 | } else { 1047 | out.List = []ChatMember{} 1048 | } 1049 | } else { 1050 | out.List = (out.List)[:0] 1051 | } 1052 | for !in.IsDelim(']') { 1053 | var v10 ChatMember 1054 | (v10).UnmarshalEasyJSON(in) 1055 | out.List = append(out.List, v10) 1056 | in.WantComma() 1057 | } 1058 | in.Delim(']') 1059 | } 1060 | default: 1061 | in.SkipRecursive() 1062 | } 1063 | in.WantComma() 1064 | } 1065 | in.Delim('}') 1066 | if isTopLevel { 1067 | in.Consumed() 1068 | } 1069 | } 1070 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang12(out *jwriter.Writer, in MembersListResponse) { 1071 | out.RawByte('{') 1072 | first := true 1073 | _ = first 1074 | { 1075 | const prefix string = ",\"members\":" 1076 | out.RawString(prefix[1:]) 1077 | if in.List == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 1078 | out.RawString("null") 1079 | } else { 1080 | out.RawByte('[') 1081 | for v11, v12 := range in.List { 1082 | if v11 > 0 { 1083 | out.RawByte(',') 1084 | } 1085 | (v12).MarshalEasyJSON(out) 1086 | } 1087 | out.RawByte(']') 1088 | } 1089 | } 1090 | out.RawByte('}') 1091 | } 1092 | 1093 | // MarshalJSON supports json.Marshaler interface 1094 | func (v MembersListResponse) MarshalJSON() ([]byte, error) { 1095 | w := jwriter.Writer{} 1096 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang12(&w, v) 1097 | return w.Buffer.BuildBytes(), w.Error 1098 | } 1099 | 1100 | // MarshalEasyJSON supports easyjson.Marshaler interface 1101 | func (v MembersListResponse) MarshalEasyJSON(w *jwriter.Writer) { 1102 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang12(w, v) 1103 | } 1104 | 1105 | // UnmarshalJSON supports json.Unmarshaler interface 1106 | func (v *MembersListResponse) UnmarshalJSON(data []byte) error { 1107 | r := jlexer.Lexer{Data: data} 1108 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang12(&r, v) 1109 | return r.Error() 1110 | } 1111 | 1112 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1113 | func (v *MembersListResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { 1114 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang12(l, v) 1115 | } 1116 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang13(in *jlexer.Lexer, out *EventPayload) { 1117 | isTopLevel := in.IsStart() 1118 | if in.IsNull() { 1119 | if isTopLevel { 1120 | in.Consumed() 1121 | } 1122 | in.Skip() 1123 | return 1124 | } 1125 | in.Delim('{') 1126 | for !in.IsDelim('}') { 1127 | key := in.UnsafeFieldName(false) 1128 | in.WantColon() 1129 | if in.IsNull() { 1130 | in.Skip() 1131 | in.WantComma() 1132 | continue 1133 | } 1134 | switch key { 1135 | case "parts": 1136 | if in.IsNull() { 1137 | in.Skip() 1138 | out.Parts = nil 1139 | } else { 1140 | in.Delim('[') 1141 | if out.Parts == nil { 1142 | if !in.IsDelim(']') { 1143 | out.Parts = make([]Part, 0, 0) 1144 | } else { 1145 | out.Parts = []Part{} 1146 | } 1147 | } else { 1148 | out.Parts = (out.Parts)[:0] 1149 | } 1150 | for !in.IsDelim(']') { 1151 | var v13 Part 1152 | (v13).UnmarshalEasyJSON(in) 1153 | out.Parts = append(out.Parts, v13) 1154 | in.WantComma() 1155 | } 1156 | in.Delim(']') 1157 | } 1158 | case "queryId": 1159 | out.QueryID = string(in.String()) 1160 | case "message": 1161 | (out.CallbackMsg).UnmarshalEasyJSON(in) 1162 | case "callbackData": 1163 | out.CallbackData = string(in.String()) 1164 | case "leftMembers": 1165 | if in.IsNull() { 1166 | in.Skip() 1167 | out.LeftMembers = nil 1168 | } else { 1169 | in.Delim('[') 1170 | if out.LeftMembers == nil { 1171 | if !in.IsDelim(']') { 1172 | out.LeftMembers = make([]Contact, 0, 1) 1173 | } else { 1174 | out.LeftMembers = []Contact{} 1175 | } 1176 | } else { 1177 | out.LeftMembers = (out.LeftMembers)[:0] 1178 | } 1179 | for !in.IsDelim(']') { 1180 | var v14 Contact 1181 | (v14).UnmarshalEasyJSON(in) 1182 | out.LeftMembers = append(out.LeftMembers, v14) 1183 | in.WantComma() 1184 | } 1185 | in.Delim(']') 1186 | } 1187 | case "newMembers": 1188 | if in.IsNull() { 1189 | in.Skip() 1190 | out.NewMembers = nil 1191 | } else { 1192 | in.Delim('[') 1193 | if out.NewMembers == nil { 1194 | if !in.IsDelim(']') { 1195 | out.NewMembers = make([]Contact, 0, 1) 1196 | } else { 1197 | out.NewMembers = []Contact{} 1198 | } 1199 | } else { 1200 | out.NewMembers = (out.NewMembers)[:0] 1201 | } 1202 | for !in.IsDelim(']') { 1203 | var v15 Contact 1204 | (v15).UnmarshalEasyJSON(in) 1205 | out.NewMembers = append(out.NewMembers, v15) 1206 | in.WantComma() 1207 | } 1208 | in.Delim(']') 1209 | } 1210 | case "addedBy": 1211 | (out.AddedBy).UnmarshalEasyJSON(in) 1212 | case "removedBy": 1213 | (out.RemovedBy).UnmarshalEasyJSON(in) 1214 | case "msgId": 1215 | out.MsgID = string(in.String()) 1216 | case "chat": 1217 | (out.Chat).UnmarshalEasyJSON(in) 1218 | case "from": 1219 | (out.From).UnmarshalEasyJSON(in) 1220 | case "text": 1221 | out.Text = string(in.String()) 1222 | case "timestamp": 1223 | out.Timestamp = int(in.Int()) 1224 | case "parent_topic": 1225 | if in.IsNull() { 1226 | in.Skip() 1227 | out.ParentMessage = nil 1228 | } else { 1229 | if out.ParentMessage == nil { 1230 | out.ParentMessage = new(ParentMessage) 1231 | } 1232 | (*out.ParentMessage).UnmarshalEasyJSON(in) 1233 | } 1234 | default: 1235 | in.SkipRecursive() 1236 | } 1237 | in.WantComma() 1238 | } 1239 | in.Delim('}') 1240 | if isTopLevel { 1241 | in.Consumed() 1242 | } 1243 | } 1244 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang13(out *jwriter.Writer, in EventPayload) { 1245 | out.RawByte('{') 1246 | first := true 1247 | _ = first 1248 | { 1249 | const prefix string = ",\"parts\":" 1250 | out.RawString(prefix[1:]) 1251 | if in.Parts == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 1252 | out.RawString("null") 1253 | } else { 1254 | out.RawByte('[') 1255 | for v16, v17 := range in.Parts { 1256 | if v16 > 0 { 1257 | out.RawByte(',') 1258 | } 1259 | (v17).MarshalEasyJSON(out) 1260 | } 1261 | out.RawByte(']') 1262 | } 1263 | } 1264 | { 1265 | const prefix string = ",\"queryId\":" 1266 | out.RawString(prefix) 1267 | out.String(string(in.QueryID)) 1268 | } 1269 | { 1270 | const prefix string = ",\"message\":" 1271 | out.RawString(prefix) 1272 | (in.CallbackMsg).MarshalEasyJSON(out) 1273 | } 1274 | { 1275 | const prefix string = ",\"callbackData\":" 1276 | out.RawString(prefix) 1277 | out.String(string(in.CallbackData)) 1278 | } 1279 | { 1280 | const prefix string = ",\"leftMembers\":" 1281 | out.RawString(prefix) 1282 | if in.LeftMembers == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 1283 | out.RawString("null") 1284 | } else { 1285 | out.RawByte('[') 1286 | for v18, v19 := range in.LeftMembers { 1287 | if v18 > 0 { 1288 | out.RawByte(',') 1289 | } 1290 | (v19).MarshalEasyJSON(out) 1291 | } 1292 | out.RawByte(']') 1293 | } 1294 | } 1295 | { 1296 | const prefix string = ",\"newMembers\":" 1297 | out.RawString(prefix) 1298 | if in.NewMembers == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 1299 | out.RawString("null") 1300 | } else { 1301 | out.RawByte('[') 1302 | for v20, v21 := range in.NewMembers { 1303 | if v20 > 0 { 1304 | out.RawByte(',') 1305 | } 1306 | (v21).MarshalEasyJSON(out) 1307 | } 1308 | out.RawByte(']') 1309 | } 1310 | } 1311 | { 1312 | const prefix string = ",\"addedBy\":" 1313 | out.RawString(prefix) 1314 | (in.AddedBy).MarshalEasyJSON(out) 1315 | } 1316 | { 1317 | const prefix string = ",\"removedBy\":" 1318 | out.RawString(prefix) 1319 | (in.RemovedBy).MarshalEasyJSON(out) 1320 | } 1321 | { 1322 | const prefix string = ",\"msgId\":" 1323 | out.RawString(prefix) 1324 | out.String(string(in.MsgID)) 1325 | } 1326 | { 1327 | const prefix string = ",\"chat\":" 1328 | out.RawString(prefix) 1329 | (in.Chat).MarshalEasyJSON(out) 1330 | } 1331 | { 1332 | const prefix string = ",\"from\":" 1333 | out.RawString(prefix) 1334 | (in.From).MarshalEasyJSON(out) 1335 | } 1336 | { 1337 | const prefix string = ",\"text\":" 1338 | out.RawString(prefix) 1339 | out.String(string(in.Text)) 1340 | } 1341 | { 1342 | const prefix string = ",\"timestamp\":" 1343 | out.RawString(prefix) 1344 | out.Int(int(in.Timestamp)) 1345 | } 1346 | { 1347 | const prefix string = ",\"parent_topic\":" 1348 | out.RawString(prefix) 1349 | if in.ParentMessage == nil { 1350 | out.RawString("null") 1351 | } else { 1352 | (*in.ParentMessage).MarshalEasyJSON(out) 1353 | } 1354 | } 1355 | out.RawByte('}') 1356 | } 1357 | 1358 | // MarshalJSON supports json.Marshaler interface 1359 | func (v EventPayload) MarshalJSON() ([]byte, error) { 1360 | w := jwriter.Writer{} 1361 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang13(&w, v) 1362 | return w.Buffer.BuildBytes(), w.Error 1363 | } 1364 | 1365 | // MarshalEasyJSON supports easyjson.Marshaler interface 1366 | func (v EventPayload) MarshalEasyJSON(w *jwriter.Writer) { 1367 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang13(w, v) 1368 | } 1369 | 1370 | // UnmarshalJSON supports json.Unmarshaler interface 1371 | func (v *EventPayload) UnmarshalJSON(data []byte) error { 1372 | r := jlexer.Lexer{Data: data} 1373 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang13(&r, v) 1374 | return r.Error() 1375 | } 1376 | 1377 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1378 | func (v *EventPayload) UnmarshalEasyJSON(l *jlexer.Lexer) { 1379 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang13(l, v) 1380 | } 1381 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang14(in *jlexer.Lexer, out *Event) { 1382 | isTopLevel := in.IsStart() 1383 | if in.IsNull() { 1384 | if isTopLevel { 1385 | in.Consumed() 1386 | } 1387 | in.Skip() 1388 | return 1389 | } 1390 | in.Delim('{') 1391 | for !in.IsDelim('}') { 1392 | key := in.UnsafeFieldName(false) 1393 | in.WantColon() 1394 | if in.IsNull() { 1395 | in.Skip() 1396 | in.WantComma() 1397 | continue 1398 | } 1399 | switch key { 1400 | case "eventId": 1401 | out.EventID = int(in.Int()) 1402 | case "type": 1403 | out.Type = EventType(in.String()) 1404 | case "payload": 1405 | (out.Payload).UnmarshalEasyJSON(in) 1406 | default: 1407 | in.SkipRecursive() 1408 | } 1409 | in.WantComma() 1410 | } 1411 | in.Delim('}') 1412 | if isTopLevel { 1413 | in.Consumed() 1414 | } 1415 | } 1416 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang14(out *jwriter.Writer, in Event) { 1417 | out.RawByte('{') 1418 | first := true 1419 | _ = first 1420 | { 1421 | const prefix string = ",\"eventId\":" 1422 | out.RawString(prefix[1:]) 1423 | out.Int(int(in.EventID)) 1424 | } 1425 | { 1426 | const prefix string = ",\"type\":" 1427 | out.RawString(prefix) 1428 | out.String(string(in.Type)) 1429 | } 1430 | { 1431 | const prefix string = ",\"payload\":" 1432 | out.RawString(prefix) 1433 | (in.Payload).MarshalEasyJSON(out) 1434 | } 1435 | out.RawByte('}') 1436 | } 1437 | 1438 | // MarshalJSON supports json.Marshaler interface 1439 | func (v Event) MarshalJSON() ([]byte, error) { 1440 | w := jwriter.Writer{} 1441 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang14(&w, v) 1442 | return w.Buffer.BuildBytes(), w.Error 1443 | } 1444 | 1445 | // MarshalEasyJSON supports easyjson.Marshaler interface 1446 | func (v Event) MarshalEasyJSON(w *jwriter.Writer) { 1447 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang14(w, v) 1448 | } 1449 | 1450 | // UnmarshalJSON supports json.Unmarshaler interface 1451 | func (v *Event) UnmarshalJSON(data []byte) error { 1452 | r := jlexer.Lexer{Data: data} 1453 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang14(&r, v) 1454 | return r.Error() 1455 | } 1456 | 1457 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1458 | func (v *Event) UnmarshalEasyJSON(l *jlexer.Lexer) { 1459 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang14(l, v) 1460 | } 1461 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang15(in *jlexer.Lexer, out *Contact) { 1462 | isTopLevel := in.IsStart() 1463 | if in.IsNull() { 1464 | if isTopLevel { 1465 | in.Consumed() 1466 | } 1467 | in.Skip() 1468 | return 1469 | } 1470 | in.Delim('{') 1471 | for !in.IsDelim('}') { 1472 | key := in.UnsafeFieldName(false) 1473 | in.WantColon() 1474 | if in.IsNull() { 1475 | in.Skip() 1476 | in.WantComma() 1477 | continue 1478 | } 1479 | switch key { 1480 | case "firstName": 1481 | out.FirstName = string(in.String()) 1482 | case "lastName": 1483 | out.LastName = string(in.String()) 1484 | case "userId": 1485 | out.ID = string(in.String()) 1486 | default: 1487 | in.SkipRecursive() 1488 | } 1489 | in.WantComma() 1490 | } 1491 | in.Delim('}') 1492 | if isTopLevel { 1493 | in.Consumed() 1494 | } 1495 | } 1496 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang15(out *jwriter.Writer, in Contact) { 1497 | out.RawByte('{') 1498 | first := true 1499 | _ = first 1500 | { 1501 | const prefix string = ",\"firstName\":" 1502 | out.RawString(prefix[1:]) 1503 | out.String(string(in.FirstName)) 1504 | } 1505 | { 1506 | const prefix string = ",\"lastName\":" 1507 | out.RawString(prefix) 1508 | out.String(string(in.LastName)) 1509 | } 1510 | { 1511 | const prefix string = ",\"userId\":" 1512 | out.RawString(prefix) 1513 | out.String(string(in.ID)) 1514 | } 1515 | out.RawByte('}') 1516 | } 1517 | 1518 | // MarshalJSON supports json.Marshaler interface 1519 | func (v Contact) MarshalJSON() ([]byte, error) { 1520 | w := jwriter.Writer{} 1521 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang15(&w, v) 1522 | return w.Buffer.BuildBytes(), w.Error 1523 | } 1524 | 1525 | // MarshalEasyJSON supports easyjson.Marshaler interface 1526 | func (v Contact) MarshalEasyJSON(w *jwriter.Writer) { 1527 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang15(w, v) 1528 | } 1529 | 1530 | // UnmarshalJSON supports json.Unmarshaler interface 1531 | func (v *Contact) UnmarshalJSON(data []byte) error { 1532 | r := jlexer.Lexer{Data: data} 1533 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang15(&r, v) 1534 | return r.Error() 1535 | } 1536 | 1537 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1538 | func (v *Contact) UnmarshalEasyJSON(l *jlexer.Lexer) { 1539 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang15(l, v) 1540 | } 1541 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang16(in *jlexer.Lexer, out *ChatMember) { 1542 | isTopLevel := in.IsStart() 1543 | if in.IsNull() { 1544 | if isTopLevel { 1545 | in.Consumed() 1546 | } 1547 | in.Skip() 1548 | return 1549 | } 1550 | in.Delim('{') 1551 | for !in.IsDelim('}') { 1552 | key := in.UnsafeFieldName(false) 1553 | in.WantColon() 1554 | if in.IsNull() { 1555 | in.Skip() 1556 | in.WantComma() 1557 | continue 1558 | } 1559 | switch key { 1560 | case "creator": 1561 | out.Creator = bool(in.Bool()) 1562 | case "admin": 1563 | out.Admin = bool(in.Bool()) 1564 | case "userId": 1565 | out.ID = string(in.String()) 1566 | default: 1567 | in.SkipRecursive() 1568 | } 1569 | in.WantComma() 1570 | } 1571 | in.Delim('}') 1572 | if isTopLevel { 1573 | in.Consumed() 1574 | } 1575 | } 1576 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang16(out *jwriter.Writer, in ChatMember) { 1577 | out.RawByte('{') 1578 | first := true 1579 | _ = first 1580 | { 1581 | const prefix string = ",\"creator\":" 1582 | out.RawString(prefix[1:]) 1583 | out.Bool(bool(in.Creator)) 1584 | } 1585 | { 1586 | const prefix string = ",\"admin\":" 1587 | out.RawString(prefix) 1588 | out.Bool(bool(in.Admin)) 1589 | } 1590 | { 1591 | const prefix string = ",\"userId\":" 1592 | out.RawString(prefix) 1593 | out.String(string(in.ID)) 1594 | } 1595 | out.RawByte('}') 1596 | } 1597 | 1598 | // MarshalJSON supports json.Marshaler interface 1599 | func (v ChatMember) MarshalJSON() ([]byte, error) { 1600 | w := jwriter.Writer{} 1601 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang16(&w, v) 1602 | return w.Buffer.BuildBytes(), w.Error 1603 | } 1604 | 1605 | // MarshalEasyJSON supports easyjson.Marshaler interface 1606 | func (v ChatMember) MarshalEasyJSON(w *jwriter.Writer) { 1607 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang16(w, v) 1608 | } 1609 | 1610 | // UnmarshalJSON supports json.Unmarshaler interface 1611 | func (v *ChatMember) UnmarshalJSON(data []byte) error { 1612 | r := jlexer.Lexer{Data: data} 1613 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang16(&r, v) 1614 | return r.Error() 1615 | } 1616 | 1617 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1618 | func (v *ChatMember) UnmarshalEasyJSON(l *jlexer.Lexer) { 1619 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang16(l, v) 1620 | } 1621 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang17(in *jlexer.Lexer, out *BotInfo) { 1622 | isTopLevel := in.IsStart() 1623 | if in.IsNull() { 1624 | if isTopLevel { 1625 | in.Consumed() 1626 | } 1627 | in.Skip() 1628 | return 1629 | } 1630 | in.Delim('{') 1631 | for !in.IsDelim('}') { 1632 | key := in.UnsafeFieldName(false) 1633 | in.WantColon() 1634 | if in.IsNull() { 1635 | in.Skip() 1636 | in.WantComma() 1637 | continue 1638 | } 1639 | switch key { 1640 | case "nick": 1641 | out.Nick = string(in.String()) 1642 | case "firstName": 1643 | out.FirstName = string(in.String()) 1644 | case "about": 1645 | out.About = string(in.String()) 1646 | case "photo": 1647 | if in.IsNull() { 1648 | in.Skip() 1649 | out.Photo = nil 1650 | } else { 1651 | in.Delim('[') 1652 | if out.Photo == nil { 1653 | if !in.IsDelim(']') { 1654 | out.Photo = make([]Photo, 0, 4) 1655 | } else { 1656 | out.Photo = []Photo{} 1657 | } 1658 | } else { 1659 | out.Photo = (out.Photo)[:0] 1660 | } 1661 | for !in.IsDelim(']') { 1662 | var v22 Photo 1663 | (v22).UnmarshalEasyJSON(in) 1664 | out.Photo = append(out.Photo, v22) 1665 | in.WantComma() 1666 | } 1667 | in.Delim(']') 1668 | } 1669 | case "userId": 1670 | out.ID = string(in.String()) 1671 | default: 1672 | in.SkipRecursive() 1673 | } 1674 | in.WantComma() 1675 | } 1676 | in.Delim('}') 1677 | if isTopLevel { 1678 | in.Consumed() 1679 | } 1680 | } 1681 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang17(out *jwriter.Writer, in BotInfo) { 1682 | out.RawByte('{') 1683 | first := true 1684 | _ = first 1685 | { 1686 | const prefix string = ",\"nick\":" 1687 | out.RawString(prefix[1:]) 1688 | out.String(string(in.Nick)) 1689 | } 1690 | { 1691 | const prefix string = ",\"firstName\":" 1692 | out.RawString(prefix) 1693 | out.String(string(in.FirstName)) 1694 | } 1695 | { 1696 | const prefix string = ",\"about\":" 1697 | out.RawString(prefix) 1698 | out.String(string(in.About)) 1699 | } 1700 | { 1701 | const prefix string = ",\"photo\":" 1702 | out.RawString(prefix) 1703 | if in.Photo == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 1704 | out.RawString("null") 1705 | } else { 1706 | out.RawByte('[') 1707 | for v23, v24 := range in.Photo { 1708 | if v23 > 0 { 1709 | out.RawByte(',') 1710 | } 1711 | (v24).MarshalEasyJSON(out) 1712 | } 1713 | out.RawByte(']') 1714 | } 1715 | } 1716 | { 1717 | const prefix string = ",\"userId\":" 1718 | out.RawString(prefix) 1719 | out.String(string(in.ID)) 1720 | } 1721 | out.RawByte('}') 1722 | } 1723 | 1724 | // MarshalJSON supports json.Marshaler interface 1725 | func (v BotInfo) MarshalJSON() ([]byte, error) { 1726 | w := jwriter.Writer{} 1727 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang17(&w, v) 1728 | return w.Buffer.BuildBytes(), w.Error 1729 | } 1730 | 1731 | // MarshalEasyJSON supports easyjson.Marshaler interface 1732 | func (v BotInfo) MarshalEasyJSON(w *jwriter.Writer) { 1733 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang17(w, v) 1734 | } 1735 | 1736 | // UnmarshalJSON supports json.Unmarshaler interface 1737 | func (v *BotInfo) UnmarshalJSON(data []byte) error { 1738 | r := jlexer.Lexer{Data: data} 1739 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang17(&r, v) 1740 | return r.Error() 1741 | } 1742 | 1743 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1744 | func (v *BotInfo) UnmarshalEasyJSON(l *jlexer.Lexer) { 1745 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang17(l, v) 1746 | } 1747 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang18(in *jlexer.Lexer, out *BaseEventPayload) { 1748 | isTopLevel := in.IsStart() 1749 | if in.IsNull() { 1750 | if isTopLevel { 1751 | in.Consumed() 1752 | } 1753 | in.Skip() 1754 | return 1755 | } 1756 | in.Delim('{') 1757 | for !in.IsDelim('}') { 1758 | key := in.UnsafeFieldName(false) 1759 | in.WantColon() 1760 | if in.IsNull() { 1761 | in.Skip() 1762 | in.WantComma() 1763 | continue 1764 | } 1765 | switch key { 1766 | case "msgId": 1767 | out.MsgID = string(in.String()) 1768 | case "chat": 1769 | (out.Chat).UnmarshalEasyJSON(in) 1770 | case "from": 1771 | (out.From).UnmarshalEasyJSON(in) 1772 | case "text": 1773 | out.Text = string(in.String()) 1774 | case "timestamp": 1775 | out.Timestamp = int(in.Int()) 1776 | case "parent_topic": 1777 | if in.IsNull() { 1778 | in.Skip() 1779 | out.ParentMessage = nil 1780 | } else { 1781 | if out.ParentMessage == nil { 1782 | out.ParentMessage = new(ParentMessage) 1783 | } 1784 | (*out.ParentMessage).UnmarshalEasyJSON(in) 1785 | } 1786 | default: 1787 | in.SkipRecursive() 1788 | } 1789 | in.WantComma() 1790 | } 1791 | in.Delim('}') 1792 | if isTopLevel { 1793 | in.Consumed() 1794 | } 1795 | } 1796 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang18(out *jwriter.Writer, in BaseEventPayload) { 1797 | out.RawByte('{') 1798 | first := true 1799 | _ = first 1800 | { 1801 | const prefix string = ",\"msgId\":" 1802 | out.RawString(prefix[1:]) 1803 | out.String(string(in.MsgID)) 1804 | } 1805 | { 1806 | const prefix string = ",\"chat\":" 1807 | out.RawString(prefix) 1808 | (in.Chat).MarshalEasyJSON(out) 1809 | } 1810 | { 1811 | const prefix string = ",\"from\":" 1812 | out.RawString(prefix) 1813 | (in.From).MarshalEasyJSON(out) 1814 | } 1815 | { 1816 | const prefix string = ",\"text\":" 1817 | out.RawString(prefix) 1818 | out.String(string(in.Text)) 1819 | } 1820 | { 1821 | const prefix string = ",\"timestamp\":" 1822 | out.RawString(prefix) 1823 | out.Int(int(in.Timestamp)) 1824 | } 1825 | { 1826 | const prefix string = ",\"parent_topic\":" 1827 | out.RawString(prefix) 1828 | if in.ParentMessage == nil { 1829 | out.RawString("null") 1830 | } else { 1831 | (*in.ParentMessage).MarshalEasyJSON(out) 1832 | } 1833 | } 1834 | out.RawByte('}') 1835 | } 1836 | 1837 | // MarshalJSON supports json.Marshaler interface 1838 | func (v BaseEventPayload) MarshalJSON() ([]byte, error) { 1839 | w := jwriter.Writer{} 1840 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang18(&w, v) 1841 | return w.Buffer.BuildBytes(), w.Error 1842 | } 1843 | 1844 | // MarshalEasyJSON supports easyjson.Marshaler interface 1845 | func (v BaseEventPayload) MarshalEasyJSON(w *jwriter.Writer) { 1846 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang18(w, v) 1847 | } 1848 | 1849 | // UnmarshalJSON supports json.Unmarshaler interface 1850 | func (v *BaseEventPayload) UnmarshalJSON(data []byte) error { 1851 | r := jlexer.Lexer{Data: data} 1852 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang18(&r, v) 1853 | return r.Error() 1854 | } 1855 | 1856 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1857 | func (v *BaseEventPayload) UnmarshalEasyJSON(l *jlexer.Lexer) { 1858 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang18(l, v) 1859 | } 1860 | func easyjson6601e8cdDecodeGithubComMailRuImBotGolang19(in *jlexer.Lexer, out *AdminsListResponse) { 1861 | isTopLevel := in.IsStart() 1862 | if in.IsNull() { 1863 | if isTopLevel { 1864 | in.Consumed() 1865 | } 1866 | in.Skip() 1867 | return 1868 | } 1869 | in.Delim('{') 1870 | for !in.IsDelim('}') { 1871 | key := in.UnsafeFieldName(false) 1872 | in.WantColon() 1873 | if in.IsNull() { 1874 | in.Skip() 1875 | in.WantComma() 1876 | continue 1877 | } 1878 | switch key { 1879 | case "admins": 1880 | if in.IsNull() { 1881 | in.Skip() 1882 | out.List = nil 1883 | } else { 1884 | in.Delim('[') 1885 | if out.List == nil { 1886 | if !in.IsDelim(']') { 1887 | out.List = make([]ChatMember, 0, 2) 1888 | } else { 1889 | out.List = []ChatMember{} 1890 | } 1891 | } else { 1892 | out.List = (out.List)[:0] 1893 | } 1894 | for !in.IsDelim(']') { 1895 | var v25 ChatMember 1896 | (v25).UnmarshalEasyJSON(in) 1897 | out.List = append(out.List, v25) 1898 | in.WantComma() 1899 | } 1900 | in.Delim(']') 1901 | } 1902 | default: 1903 | in.SkipRecursive() 1904 | } 1905 | in.WantComma() 1906 | } 1907 | in.Delim('}') 1908 | if isTopLevel { 1909 | in.Consumed() 1910 | } 1911 | } 1912 | func easyjson6601e8cdEncodeGithubComMailRuImBotGolang19(out *jwriter.Writer, in AdminsListResponse) { 1913 | out.RawByte('{') 1914 | first := true 1915 | _ = first 1916 | { 1917 | const prefix string = ",\"admins\":" 1918 | out.RawString(prefix[1:]) 1919 | if in.List == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { 1920 | out.RawString("null") 1921 | } else { 1922 | out.RawByte('[') 1923 | for v26, v27 := range in.List { 1924 | if v26 > 0 { 1925 | out.RawByte(',') 1926 | } 1927 | (v27).MarshalEasyJSON(out) 1928 | } 1929 | out.RawByte(']') 1930 | } 1931 | } 1932 | out.RawByte('}') 1933 | } 1934 | 1935 | // MarshalJSON supports json.Marshaler interface 1936 | func (v AdminsListResponse) MarshalJSON() ([]byte, error) { 1937 | w := jwriter.Writer{} 1938 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang19(&w, v) 1939 | return w.Buffer.BuildBytes(), w.Error 1940 | } 1941 | 1942 | // MarshalEasyJSON supports easyjson.Marshaler interface 1943 | func (v AdminsListResponse) MarshalEasyJSON(w *jwriter.Writer) { 1944 | easyjson6601e8cdEncodeGithubComMailRuImBotGolang19(w, v) 1945 | } 1946 | 1947 | // UnmarshalJSON supports json.Unmarshaler interface 1948 | func (v *AdminsListResponse) UnmarshalJSON(data []byte) error { 1949 | r := jlexer.Lexer{Data: data} 1950 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang19(&r, v) 1951 | return r.Error() 1952 | } 1953 | 1954 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 1955 | func (v *AdminsListResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { 1956 | easyjson6601e8cdDecodeGithubComMailRuImBotGolang19(l, v) 1957 | } 1958 | --------------------------------------------------------------------------------