diff options
Diffstat (limited to 'vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/longpoll.go')
-rw-r--r-- | vendor/github.com/SevereCloud/vksdk/v2/longpoll-bot/longpoll.go | 219 |
1 files changed, 219 insertions, 0 deletions
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) +} |