4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-06-27 12:19:23 +00:00

Add vk bridge (#1372)

* Add vk bridge

* Vk bridge attachments

* Vk bridge forwarded messages

* Vk bridge sample config and code cleanup

* Vk bridge add vendor

* Vk bridge message edit

* Vk bridge: fix fetching names of other bots

* Vk bridge: code cleanup

* Vk bridge: fix shadows declaration

* Vk bridge: remove UseFileURL
This commit is contained in:
Ivanik
2021-01-29 04:25:14 +05:00
committed by GitHub
parent 5dd15ef8e7
commit 8764be7461
112 changed files with 26908 additions and 0 deletions

View File

@ -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()
}
```

View File

@ -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)
}

View File

@ -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,
)
}

View File

@ -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)
}