diff options
Diffstat (limited to 'vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot')
4 files changed, 381 insertions, 0 deletions
diff --git a/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/README.md b/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/README.md new file mode 100644 index 00000000..94d87dba --- /dev/null +++ b/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/README.md @@ -0,0 +1,132 @@ +# Bots Long Poll API + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/SevereCloud/vksdk/v2/longpoll-bot)](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/longpoll-bot) +[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://vk.com/dev/bots_longpoll) + +## Подключение Bots Long Poll API + +Long Poll настраивается автоматически. Вам не требуется заходить в настройки +сообщества. + +### Версия API + +Данная библиотека поддерживает версию API **5.122**. + +### Инициализация + +Модуль можно использовать с ключом доступа пользователя, полученным в +Standalone-приложении через Implicit Flow(требуются права доступа: **groups**) +или с ключом доступа сообщества(требуются права доступа: **manage**). + +В начале необходимо инициализировать api: + +```go +vk := api.NewVK("<TOKEN>") +``` + +А потом сам longpoll + +```go +lp, err := longpoll.NewLongPoll(vk api.VK, groupID int) +// По умолчанию Wait = 25 +// lp.Wait = 90 +// lp.Ts = "123" +``` + +### HTTP client + +В модуле реализована возможность изменять HTTP клиент - `lp.Client` + +Пример прокси + +```go +dialer, _ := proxy.SOCKS5("tcp", "127.0.0.1:9050", nil, proxy.Direct) +httpTransport := &http.Transport{ + Dial: dialer.Dial, + // DisableKeepAlives: true, +} +httpTransport.Dial = dialer.Dial +lp.Client.Transport = httpTransport +``` + +### Обработчик событий + +Для каждого события существует отдельный обработчик, который передает функции +`ctx` и `object`. + +Пример для события `message_new` + +```go +lp.MessageNew(func(ctx context.Context, obj events.MessageNewObject) { + ... +}) +``` + +Если вы хотите получать полный ответ от Long Poll(например для сохранения `ts` +или специальной обработки `failed`), можно воспользоваться следующим обработчиком. + +```go +lp.FullResponse(func(resp object.LongPollBotResponse) { + ... +}) +``` + +Полный список событий Вы найдёте [в документации](https://vk.com/dev/groups_events) + +### Контекст + +Поля `groupID`, `ts` и `eventID` передаются в `ctx`. Чтобы получить их, можно +воспользоваться следующими функциями: + +```go +groupID := events.GroupIDFromContext(ctx) +eventID := events.EventIDFromContext(ctx) +ts := longpoll.TsFromContext(ctx) +``` + +### Запуск и остановка + +```go +// Запуск +if err := lp.Run(); err != nil { + log.Fatal(err) +} + +// Безопасное завершение +// Ждет пока соединение закроется и события обработаются +lp.Shutdown() + +// Закрыть соединение +// Требует lp.Client.Transport = &http.Transport{DisableKeepAlives: true} +lp.Client.CloseIdleConnections() +``` + +## Пример + +```go +package main + +import ( + "log" + + "github.com/SevereCloud/vksdk/v2/api" + + longpoll "github.com/SevereCloud/vksdk/v2/longpoll-bot" + "github.com/SevereCloud/vksdk/v2/events" +) + +func main() { + vk := api.NewVK("<TOKEN>") + lp, err := longpoll.NewLongPoll(vk, 12345678) + if err != nil { + panic(err) + } + + lp.MessageNew(func(ctx context.Context, obj events.MessageNewObject) { + log.Print(obj.Message.Text) + }) + + lp.Run() +} + +``` diff --git a/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/context.go b/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/context.go new file mode 100644 index 00000000..cfc4fd90 --- /dev/null +++ b/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/context.go @@ -0,0 +1,12 @@ +package longpoll // import "github.com/SevereCloud/vksdk/v2/longpoll-bot" + +import ( + "context" + + "github.com/SevereCloud/vksdk/v2/internal" +) + +// TsFromContext returns the ts from context. +func TsFromContext(ctx context.Context) int { + return ctx.Value(internal.LongPollTsKey).(int) +} diff --git a/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/errors.go b/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/errors.go new file mode 100644 index 00000000..58d97c96 --- /dev/null +++ b/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/errors.go @@ -0,0 +1,18 @@ +package longpoll + +import ( + "fmt" +) + +// Failed struct. +type Failed struct { + Code int +} + +// Error returns the message of a Failed. +func (e Failed) Error() string { + return fmt.Sprintf( + "longpoll: failed code %d", + e.Code, + ) +} diff --git a/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/longpoll.go b/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/longpoll.go new file mode 100644 index 00000000..b230c4c1 --- /dev/null +++ b/vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/longpoll.go @@ -0,0 +1,219 @@ +/* +Package longpoll implements Bots Long Poll API. + +See more https://vk.com/dev/bots_longpoll +*/ +package longpoll // import "github.com/SevereCloud/vksdk/v2/longpoll-bot" + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/SevereCloud/vksdk/v2" + "github.com/SevereCloud/vksdk/v2/api" + "github.com/SevereCloud/vksdk/v2/events" + "github.com/SevereCloud/vksdk/v2/internal" +) + +// Response struct. +type Response struct { + Ts string `json:"ts"` + Updates []events.GroupEvent `json:"updates"` + Failed int `json:"failed"` +} + +// LongPoll struct. +type LongPoll struct { + GroupID int + Server string + Key string + Ts string + Wait int + VK *api.VK + Client *http.Client + cancel context.CancelFunc + + funcFullResponseList []func(Response) + + events.FuncList +} + +// NewLongPoll returns a new LongPoll. +// +// The LongPoll will use the http.DefaultClient. +// This means that if the http.DefaultClient is modified by other components +// of your application the modifications will be picked up by the SDK as well. +func NewLongPoll(vk *api.VK, groupID int) (*LongPoll, error) { + lp := &LongPoll{ + VK: vk, + GroupID: groupID, + Wait: 25, + Client: http.DefaultClient, + } + lp.FuncList = *events.NewFuncList() + + err := lp.updateServer(true) + + return lp, err +} + +// NewLongPollCommunity returns a new LongPoll for community token. +// +// The LongPoll will use the http.DefaultClient. +// This means that if the http.DefaultClient is modified by other components +// of your application the modifications will be picked up by the SDK as well. +func NewLongPollCommunity(vk *api.VK) (*LongPoll, error) { + resp, err := vk.GroupsGetByID(nil) + if err != nil { + return nil, err + } + + lp := &LongPoll{ + VK: vk, + GroupID: resp[0].ID, + Wait: 25, + Client: http.DefaultClient, + } + lp.FuncList = *events.NewFuncList() + + err = lp.updateServer(true) + + return lp, err +} + +func (lp *LongPoll) updateServer(updateTs bool) error { + params := api.Params{ + "group_id": lp.GroupID, + } + + serverSetting, err := lp.VK.GroupsGetLongPollServer(params) + if err != nil { + return err + } + + lp.Key = serverSetting.Key + lp.Server = serverSetting.Server + + if updateTs { + lp.Ts = serverSetting.Ts + } + + return nil +} + +func (lp *LongPoll) check(ctx context.Context) (response Response, err error) { + u := fmt.Sprintf("%s?act=a_check&key=%s&ts=%s&wait=%d", lp.Server, lp.Key, lp.Ts, lp.Wait) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) + if err != nil { + return response, err + } + + resp, err := lp.Client.Do(req) + if err != nil { + return response, err + } + defer resp.Body.Close() + + err = json.NewDecoder(resp.Body).Decode(&response) + if err != nil { + return response, err + } + + err = lp.checkResponse(response) + + return response, err +} + +func (lp *LongPoll) checkResponse(response Response) (err error) { + switch response.Failed { + case 0: + lp.Ts = response.Ts + case 1: + lp.Ts = response.Ts + case 2: + err = lp.updateServer(false) + case 3: + err = lp.updateServer(true) + default: + err = &Failed{response.Failed} + } + + return +} + +func (lp *LongPoll) autoSetting(ctx context.Context) error { + params := api.Params{ + "group_id": lp.GroupID, + "enabled": true, + "api_version": vksdk.API, + }.WithContext(ctx) + for _, event := range lp.ListEvents() { + params[string(event)] = true + } + + // Updating LongPoll settings + _, err := lp.VK.GroupsSetLongPollSettings(params) + + return err +} + +// Run handler. +func (lp *LongPoll) Run() error { + return lp.RunWithContext(context.Background()) +} + +// RunWithContext handler. +func (lp *LongPoll) RunWithContext(ctx context.Context) error { + return lp.run(ctx) +} + +func (lp *LongPoll) run(ctx context.Context) error { + ctx, lp.cancel = context.WithCancel(ctx) + + err := lp.autoSetting(ctx) + if err != nil { + return err + } + + for { + select { + case _, ok := <-ctx.Done(): + if !ok { + return nil + } + default: + resp, err := lp.check(ctx) + if err != nil { + return err + } + + ctx = context.WithValue(ctx, internal.LongPollTsKey, resp.Ts) + + for _, event := range resp.Updates { + err = lp.Handler(ctx, event) + if err != nil { + return err + } + } + + for _, f := range lp.funcFullResponseList { + f(resp) + } + } + } +} + +// Shutdown gracefully shuts down the longpoll without interrupting any active connections. +func (lp *LongPoll) Shutdown() { + if lp.cancel != nil { + lp.cancel() + } +} + +// FullResponse handler. +func (lp *LongPoll) FullResponse(f func(Response)) { + lp.funcFullResponseList = append(lp.funcFullResponseList, f) +} |