commit 03a2efc04972ceedc2634e3c82621fdcdb6b26d5
Author: Ivanik <ivan170102@gmail.com>
Date: Fri Jan 29 04:25:14 2021 +0000
diff --git a/README.md b/README.md
index 8c631a5..71fec31 100644
--- a/README.md
+++ b/README.md
@@ -1026 +1027 @@ And more...
- Not supported anymore, see [here](https://github.com/Philipp15b/go-steam/issues/94) for more info.
- [Telegram](https://telegram.org)
- [Twitch](https://twitch.tv)
+- [VK](https://vk.com/)
- [WhatsApp](https://www.whatsapp.com/)
- [XMPP](https://xmpp.org)
- [Zulip](https://zulipchat.com)
@@ -3486 +3497 @@ Matterbridge wouldn't exist without these libraries:
- steam - <https://github.com/Philipp15b/go-steam>
- telegram - <https://github.com/go-telegram-bot-api/telegram-bot-api>
- tengo - <https://github.com/d5/tengo>
+- vk - <https://github.com/SevereCloud/vksdk>
- whatsapp - <https://github.com/Rhymen/go-whatsapp>
- xmpp - <https://github.com/mattn/go-xmpp>
- zulip - <https://github.com/ifo/gozulipbot>
diff --git a/bridge/vk/vk.go b/bridge/vk/vk.go
new file mode 100644
index 0000000..89a653c
--- /dev/null
+++ b/bridge/vk/vk.go
@@ -00 +1327 @@
+package bvk
+
+import (
+ "bytes"
+ "context"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/42wim/matterbridge/bridge"
+ "github.com/42wim/matterbridge/bridge/config"
+ "github.com/42wim/matterbridge/bridge/helper"
+
+ "github.com/SevereCloud/vksdk/v2/api"
+ "github.com/SevereCloud/vksdk/v2/events"
+ longpoll "github.com/SevereCloud/vksdk/v2/longpoll-bot"
+ "github.com/SevereCloud/vksdk/v2/object"
+)
+
+const (
+ audioMessage = "audio_message"
+ document = "doc"
+ photo = "photo"
+ video = "video"
+ graffiti = "graffiti"
+ sticker = "sticker"
+ wall = "wall"
+)
+
+type user struct {
+ lastname, firstname, avatar string
+}
+
+type Bvk struct {
+ c *api.VK
+ usernamesMap map[int]user // cache of user names and avatar URLs
+ *bridge.Config
+}
+
+func New(cfg *bridge.Config) bridge.Bridger {
+ return &Bvk{usernamesMap: make(map[int]user), Config: cfg}
+}
+
+func (b *Bvk) Connect() error {
+ b.Log.Info("Connecting")
+ b.c = api.NewVK(b.GetString("Token"))
+ lp, err := longpoll.NewLongPoll(b.c, b.GetInt("GroupID"))
+ if err != nil {
+ b.Log.Debugf("%#v", err)
+
+ return err
+ }
+
+ lp.MessageNew(func(ctx context.Context, obj events.MessageNewObject) {
+ b.handleMessage(obj.Message, false)
+ })
+
+ b.Log.Info("Connection succeeded")
+
+ go func() {
+ err := lp.Run()
+ if err != nil {
+ b.Log.Fatal("Enable longpoll in group management")
+ }
+ }()
+
+ return nil
+}
+
+func (b *Bvk) Disconnect() error {
+ return nil
+}
+
+func (b *Bvk) JoinChannel(channel config.ChannelInfo) error {
+ return nil
+}
+
+func (b *Bvk) Send(msg config.Message) (string, error) {
+ b.Log.Debugf("=> Receiving %#v", msg)
+
+ peerID, err := strconv.Atoi(msg.Channel)
+ if err != nil {
+ return "", err
+ }
+
+ params := api.Params{}
+
+ text := msg.Username + msg.Text
+
+ if msg.Extra != nil {
+ if len(msg.Extra["file"]) > 0 {
+ // generate attachments string
+ attachment, urls := b.uploadFiles(msg.Extra, peerID)
+ params["attachment"] = attachment
+ text += urls
+ }
+ }
+
+ params["message"] = text
+
+ if msg.ID == "" {
+ // New message
+ params["random_id"] = time.Now().Unix()
+ params["peer_ids"] = msg.Channel
+
+ res, e := b.c.MessagesSendPeerIDs(params)
+ if e != nil {
+ return "", err
+ }
+
+ return strconv.Itoa(res[0].ConversationMessageID), nil
+ }
+ // Edit message
+ messageID, err := strconv.ParseInt(msg.ID, 10, 64)
+ if err != nil {
+ return "", err
+ }
+
+ params["peer_id"] = peerID
+ params["conversation_message_id"] = messageID
+
+ _, err = b.c.MessagesEdit(params)
+ if err != nil {
+ return "", err
+ }
+
+ return msg.ID, nil
+}
+
+func (b *Bvk) getUser(id int) user {
+ u, found := b.usernamesMap[id]
+ if !found {
+ b.Log.Debug("Fetching username for ", id)
+
+ if id >= 0 {
+ result, _ := b.c.UsersGet(api.Params{
+ "user_ids": id,
+ "fields": "photo_200",
+ })
+
+ resUser := result[0]
+ u = user{lastname: resUser.LastName, firstname: resUser.FirstName, avatar: resUser.Photo200}
+ b.usernamesMap[id] = u
+ } else {
+ result, _ := b.c.GroupsGetByID(api.Params{
+ "group_id": id * -1,
+ })
+
+ resGroup := result[0]
+ u = user{lastname: resGroup.Name, avatar: resGroup.Photo200}
+ }
+ }
+
+ return u
+}
+
+func (b *Bvk) handleMessage(msg object.MessagesMessage, isFwd bool) {
+ b.Log.Debug("ChatID: ", msg.PeerID)
+ // fetch user info
+ u := b.getUser(msg.FromID)
+
+ rmsg := config.Message{
+ Text: msg.Text,
+ Username: u.firstname + " " + u.lastname,
+ Avatar: u.avatar,
+ Channel: strconv.Itoa(msg.PeerID),
+ Account: b.Account,
+ UserID: strconv.Itoa(msg.FromID),
+ ID: strconv.Itoa(msg.ConversationMessageID),
+ Extra: make(map[string][]interface{}),
+ }
+
+ if msg.ReplyMessage != nil {
+ ur := b.getUser(msg.ReplyMessage.FromID)
+ rmsg.Text = "Re: " + ur.firstname + " " + ur.lastname + "\n" + rmsg.Text
+ }
+
+ if isFwd {
+ rmsg.Username = "Fwd: " + rmsg.Username
+ }
+
+ if len(msg.Attachments) > 0 {
+ urls, text := b.getFiles(msg.Attachments)
+
+ if text != "" {
+ rmsg.Text += "\n" + text
+ }
+
+ // download
+ b.downloadFiles(&rmsg, urls)
+ }
+
+ if len(msg.FwdMessages) > 0 {
+ rmsg.Text += strconv.Itoa(len(msg.FwdMessages)) + " forwarded messages"
+ }
+
+ b.Remote <- rmsg
+
+ if len(msg.FwdMessages) > 0 {
+ // recursive processing of forwarded messages
+ for _, m := range msg.FwdMessages {
+ m.PeerID = msg.PeerID
+ b.handleMessage(m, true)
+ }
+ }
+}
+
+func (b *Bvk) uploadFiles(extra map[string][]interface{}, peerID int) (string, string) {
+ var attachments []string
+ text := ""
+
+ for _, f := range extra["file"] {
+ fi := f.(config.FileInfo)
+
+ if fi.Comment != "" {
+ text += fi.Comment + "\n"
+ }
+ a, err := b.uploadFile(fi, peerID)
+ if err != nil {
+ b.Log.Error("File upload error ", fi.Name)
+ }
+
+ attachments = append(attachments, a)
+ }
+
+ return strings.Join(attachments, ","), text
+}
+
+func (b *Bvk) uploadFile(file config.FileInfo, peerID int) (string, error) {
+ r := bytes.NewReader(*file.Data)
+
+ photoRE := regexp.MustCompile(".(jpg|jpe|png)$")
+ if photoRE.MatchString(file.Name) {
+ p, err := b.c.UploadMessagesPhoto(peerID, r)
+ if err != nil {
+ return "", err
+ }
+
+ return photo + strconv.Itoa(p[0].OwnerID) + "_" + strconv.Itoa(p[0].ID), nil
+ }
+
+ var doctype string
+ if strings.Contains(file.Name, ".ogg") {
+ doctype = audioMessage
+ } else {
+ doctype = document
+ }
+
+ doc, err := b.c.UploadMessagesDoc(peerID, doctype, file.Name, "", r)
+ if err != nil {
+ return "", err
+ }
+
+ switch doc.Type {
+ case audioMessage:
+ return document + strconv.Itoa(doc.AudioMessage.OwnerID) + "_" + strconv.Itoa(doc.AudioMessage.ID), nil
+ case document:
+ return document + strconv.Itoa(doc.Doc.OwnerID) + "_" + strconv.Itoa(doc.Doc.ID), nil
+ }
+
+ return "", nil
+}
+
+func (b *Bvk) getFiles(attachments []object.MessagesMessageAttachment) ([]string, string) {
+ var urls []string
+ var text []string
+
+ for _, a := range attachments {
+ switch a.Type {
+ case photo:
+ var resolution float64 = 0
+ url := a.Photo.Sizes[0].URL
+ for _, size := range a.Photo.Sizes {
+ r := size.Height * size.Width
+ if resolution < r {
+ resolution = r
+ url = size.URL
+ }
+ }
+
+ urls = append(urls, url)
+
+ case document:
+ urls = append(urls, a.Doc.URL)
+
+ case graffiti:
+ urls = append(urls, a.Graffiti.URL)
+
+ case audioMessage:
+ urls = append(urls, a.AudioMessage.DocsDocPreviewAudioMessage.LinkOgg)
+
+ case sticker:
+ var resolution float64 = 0
+ url := a.Sticker.Images[0].URL
+ for _, size := range a.Sticker.Images {
+ r := size.Height * size.Width
+ if resolution < r {
+ resolution = r
+ url = size.URL
+ }
+ }
+ urls = append(urls, url+".png")
+ case video:
+ text = append(text, "https://vk.com/video"+strconv.Itoa(a.Video.OwnerID)+"_"+strconv.Itoa(a.Video.ID))
+
+ case wall:
+ text = append(text, "https://vk.com/wall"+strconv.Itoa(a.Wall.FromID)+"_"+strconv.Itoa(a.Wall.ID))
+
+ default:
+ text = append(text, "This attachment is not supported ("+a.Type+")")
+ }
+ }
+
+ return urls, strings.Join(text, "\n")
+}
+
+func (b *Bvk) downloadFiles(rmsg *config.Message, urls []string) {
+ for _, url := range urls {
+ data, err := helper.DownloadFile(url)
+ if err == nil {
+ urlPart := strings.Split(url, "/")
+ name := strings.Split(urlPart[len(urlPart)-1], "?")[0]
+ helper.HandleDownloadData(b.Log, rmsg, name, "", url, data, b.General)
+ }
+ }
+}
diff --git a/gateway/bridgemap/bvk.go b/gateway/bridgemap/bvk.go
new file mode 100644
index 0000000..ea3de19
--- /dev/null
+++ b/gateway/bridgemap/bvk.go
@@ -00 +111 @@
+// +build !novk
+
+package bridgemap
+
+import (
+ bvk "github.com/42wim/matterbridge/bridge/vk"
+)
+
+func init() {
+ FullMap["vk"] = bvk.New
+}
diff --git a/go.mod b/go.mod
index a4c9cd9..2aa6546 100644
--- a/go.mod
+++ b/go.mod
@@ -66 +67 @@ require (
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
github.com/Rhymen/go-whatsapp v0.1.2-0.20201226125722-8029c28f5c5a
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20200922220614-e4a51dfb52e4 // indirect
+ github.com/SevereCloud/vksdk/v2 v2.9.0
github.com/d5/tengo/v2 v2.6.2
github.com/davecgh/go-spew v1.1.1
github.com/fsnotify/fsnotify v1.4.9
diff --git a/go.sum b/go.sum
index 3a24098..b729ac2 100644
--- a/go.sum
+++ b/go.sum
@@ -876 +878 @@ github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06
github.com/RoaringBitmap/roaring v0.5.1/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20200922220614-e4a51dfb52e4 h1:u7UvmSK6McEMXFZB310/YZ6uvfDaSFrSoqWoy/qaOW0=
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20200922220614-e4a51dfb52e4/go.mod h1:wVff6N8s2foRPCYeynerOM/FF44uyI60/HMiboL0SXw=
+github.com/SevereCloud/vksdk/v2 v2.9.0 h1:39qjzmozK5FDfnDkfA+YN0CtKi4mDrzjPtoT5GN9Xg0=
+github.com/SevereCloud/vksdk/v2 v2.9.0/go.mod h1:IBmfJ3rs+zDLD9NHCoJEpgg5A4UOoxgUU/g8p5lYb48=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
diff --git a/matterbridge.toml.sample b/matterbridge.toml.sample
index a398961..6ee6ed5 100644
--- a/matterbridge.toml.sample
+++ b/matterbridge.toml.sample
@@ -14866 +148618 @@ TLSCACertificate=mumble-ca.crt
# OPTIONAL (default false)
SkipTLSVerify=false
+###################################################################
+#VK
+###################################################################
+[vk.myvk]
+#Group access token
+#See https://vk.com/dev/bots_docs
+Token="Yourtokenhere"
+
+#Group ID
+#For example in URL https://vk.com/public168963511 group ID is 168963511
+GroupID=123456789
+
###################################################################
#
# WhatsApp
@@ -18436 +18558 @@ enable=true
+ # vk | peerid | 2000000002 | A number that starts form 2000000000. Use --debug and send any message in chat to get PeerID in the logs
+ # -------------------------------------------------------------------------------------------------------------------------------------