Compare commits

..

23 Commits

Author SHA1 Message Date
Wim
6c442e239d Release v1.17.4 (#1112) 2020-04-21 23:53:51 +02:00
Wim
eaf92fca4d Add an ID cache (discord). Fixes #1106 (#1111)
When a webhook "edits" a message, it does this by deleting the message
and creating a new one with the new content.

On creation of this new message, we'll get another ID then already is
know by the gateway in its id cache. So we add it in our own cache and
replace it whenever we want to edit/delete it again.
2020-04-21 23:35:46 +02:00
Wim
06b7bad714 Lowercase account names. Fixes #1108 (#1110) 2020-04-21 20:42:11 +02:00
Wim
19eec2ed03 Update Rhymen/go-whatsapp. Fixes #1107 (#1109) 2020-04-21 19:55:47 +02:00
Wim
d99c54343a Remove panics and retry polling on failure (msteams). Fixes #1104 (#1105) 2020-04-21 19:29:24 +02:00
Wim
308a110000 Bump version 2020-04-19 17:23:17 +02:00
Wim
4f406b2ce6 Release v1.17.3 (#1103) 2020-04-19 17:13:58 +02:00
Wim
e564c555d7 Clip too long messages on 3000 length (slack). Fixes #1081 (#1102) 2020-04-19 17:00:11 +02:00
Wim
f7ec9af9e8 Add extra space before colon in attachments (irc). Fixes #1089 (#1101) 2020-04-19 16:45:53 +02:00
Wim
4d93a774ce Ignore non-critical errors (whatsapp). Fixes #1094 (#1100) 2020-04-19 13:45:35 +02:00
Wim
2595dd30bf Update matterbridge/go-xmpp. Fixes #1097 (#1099) 2020-04-19 01:06:44 +02:00
Wim
9190365289 Add JoinDelay option (irc). Fixes #1084 (#1098) 2020-04-19 00:46:35 +02:00
Wim
57794b3b9f Prevent image/message looping (slack). Fixes #1088 (#1096)
Also check for our matterbridge ID in Blocks set in SubMessages.
2020-04-18 22:30:49 +02:00
ldruschk
3c36f651be Fix the behavior of ShowTopicChange and SyncTopic (#1086)
Currently, the "topic_change" events are ignored if both, 
ShowTopicChange and SyncTopic are set, and forwarded otherwise. 

This pull requests changes the behavior such that the events are 
only forwarded if one of those two config options is set to true 
and ignored otherwise.
2020-04-18 22:05:27 +02:00
ldruschk
8e6ddadba2 Relay Joins/Topic changes in RocketChat bridge (#1085)
This pull request properly sets the events EventJoinLeave and EventTopicChange for messages from the RocketChat bridge and drops messages which are neither one of those events nor plain messages.
2020-04-18 22:00:35 +02:00
Wim
8a87a71927 Update matterbridge/go-xmpp to add PEP-0030 support (#1095) 2020-04-18 20:58:55 +02:00
Qais Patankar
0047e6f523 Sort README bridge and library links (#1093) 2020-04-18 18:12:16 +02:00
Alexander
7183095a28 Implement User Avatar spoofing of XMPP users (#1090)
* Implement User Avatar spoofing of XMPP users
2020-04-16 22:16:25 +02:00
Wim
13c90893c7 Update matterbridge/Rocket.Chat.Go.SDK (#1087) 2020-04-16 21:48:53 +02:00
Wim
976fbcd07f Bump version 2020-04-09 23:03:45 +02:00
Wim
d97b077e85 Release v1.17.2 (#1080) 2020-04-09 22:54:51 +02:00
Wim
8950575bfb Update Rhymen/go-whatsapp vendor and whatsapp version (#1078) 2020-04-09 22:30:08 +02:00
Jerry Heiselman
11fc4c286f Clarify terminology used in mapping group chat IDs to channels in config (#1079)
* Clarify embedded docs for channel specification

Should help with #1072
2020-04-08 23:52:38 +02:00
37 changed files with 1741 additions and 724 deletions

View File

@@ -9,20 +9,20 @@ Letting people be where they want to be.<br />
<sup> <sup>
[Discord][mb-discord] |
[Gitter][mb-gitter] | [Gitter][mb-gitter] |
[IRC][mb-irc] | [IRC][mb-irc] |
[Discord][mb-discord] | [Keybase][mb-keybase] |
[Matrix][mb-matrix] | [Matrix][mb-matrix] |
[Slack][mb-slack] |
[Mattermost][mb-mattermost] | [Mattermost][mb-mattermost] |
[MSTeams][mb-msteams] |
[Rocket.Chat][mb-rocketchat] | [Rocket.Chat][mb-rocketchat] |
[XMPP][mb-xmpp] | [Slack][mb-slack] |
[Telegram][mb-telegram] |
[Twitch][mb-twitch] | [Twitch][mb-twitch] |
[WhatsApp][mb-whatsapp] | [WhatsApp][mb-whatsapp] |
[XMPP][mb-xmpp] |
[Zulip][mb-zulip] | [Zulip][mb-zulip] |
[Telegram][mb-telegram] |
[Keybase][mb-keybase] |
[MSTeams][mb-msteams] |
And more... And more...
</sup> </sup>
@@ -87,29 +87,29 @@ And more...
### Natively supported ### Natively supported
- [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
- [IRC](http://www.mirc.com/servers.html)
- [XMPP](https://xmpp.org)
- [Gitter](https://gitter.im)
- [Slack](https://slack.com)
- [Discord](https://discordapp.com) - [Discord](https://discordapp.com)
- [Telegram](https://telegram.org) - [Gitter](https://gitter.im)
- [Rocket.chat](https://rocket.chat) - [IRC](http://www.mirc.com/servers.html)
- [Matrix](https://matrix.org)
- [Microsoft Teams](https://teams.microsoft.com)
- [Steam](https://store.steampowered.com/)
- [Twitch](https://twitch.tv)
- [Ssh-chat](https://github.com/shazow/ssh-chat)
- [WhatsApp](https://www.whatsapp.com/)
- [Zulip](https://zulipchat.com)
- [Keybase](https://keybase.io) - [Keybase](https://keybase.io)
- [Matrix](https://matrix.org)
- [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
- [Microsoft Teams](https://teams.microsoft.com)
- [Rocket.chat](https://rocket.chat)
- [Slack](https://slack.com)
- [Ssh-chat](https://github.com/shazow/ssh-chat)
- [Steam](https://store.steampowered.com/)
- [Telegram](https://telegram.org)
- [Twitch](https://twitch.tv)
- [WhatsApp](https://www.whatsapp.com/)
- [XMPP](https://xmpp.org)
- [Zulip](https://zulipchat.com)
### 3rd party via matterbridge api ### 3rd party via matterbridge api
- [Discourse](https://github.com/DeclanHoare/matterbabble)
- [Facebook messenger](https://github.com/VictorNine/fbridge)
- [Minecraft](https://github.com/elytra/MatterLink) - [Minecraft](https://github.com/elytra/MatterLink)
- [Reddit](https://github.com/bonehurtingjuice/mattereddit) - [Reddit](https://github.com/bonehurtingjuice/mattereddit)
- [Facebook messenger](https://github.com/VictorNine/fbridge)
- [Discourse](https://github.com/DeclanHoare/matterbabble)
- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430) - [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
### API ### API
@@ -130,18 +130,18 @@ Used by the projects below. Feel free to make a PR to add your project to this l
Questions or want to test on your favorite platform? Join below: Questions or want to test on your favorite platform? Join below:
- [Discord][mb-discord]
- [Gitter][mb-gitter] - [Gitter][mb-gitter]
- [IRC][mb-irc] - [IRC][mb-irc]
- [Discord][mb-discord] - [Keybase][mb-keybase]
- [Matrix][mb-matrix] - [Matrix][mb-matrix]
- [Slack][mb-slack]
- [Mattermost][mb-mattermost] - [Mattermost][mb-mattermost]
- [Rocket.Chat][mb-rocketchat] - [Rocket.Chat][mb-rocketchat]
- [XMPP][mb-xmpp] (matterbridge@conference.jabber.de) - [Slack][mb-slack]
- [Twitch][mb-twitch]
- [Zulip][mb-zulip]
- [Telegram][mb-telegram] - [Telegram][mb-telegram]
- [Keybase][mb-keybase] - [Twitch][mb-twitch]
- [XMPP][mb-xmpp] (matterbridge@conference.jabber.de)
- [Zulip][mb-zulip]
## Screenshots ## Screenshots
@@ -151,7 +151,7 @@ See https://github.com/42wim/matterbridge/wiki
### Binaries ### Binaries
- Latest stable release [v1.17.1](https://github.com/42wim/matterbridge/releases/latest) - Latest stable release [v1.17.4](https://github.com/42wim/matterbridge/releases/latest)
- Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/) - Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration. To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
@@ -324,32 +324,32 @@ Matterbridge wouldn't exist without these libraries:
- gops - https://github.com/google/gops - gops - https://github.com/google/gops
- gozulipbot - https://github.com/ifo/gozulipbot - gozulipbot - https://github.com/ifo/gozulipbot
- irc - https://github.com/lrstanley/girc - irc - https://github.com/lrstanley/girc
- mattermost - https://github.com/mattermost/mattermost-server - keybase - https://github.com/keybase/go-keybase-chat-bot
- matrix - https://github.com/matrix-org/gomatrix - matrix - https://github.com/matrix-org/gomatrix
- sshchat - https://github.com/shazow/ssh-chat - mattermost - https://github.com/mattermost/mattermost-server
- msgraph.go - https://github.com/yaegashi/msgraph.go
- slack - https://github.com/nlopes/slack - slack - https://github.com/nlopes/slack
- sshchat - https://github.com/shazow/ssh-chat
- steam - https://github.com/Philipp15b/go-steam - steam - https://github.com/Philipp15b/go-steam
- telegram - https://github.com/go-telegram-bot-api/telegram-bot-api - telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
- xmpp - https://github.com/mattn/go-xmpp
- whatsapp - https://github.com/Rhymen/go-whatsapp/
- zulip - https://github.com/ifo/gozulipbot
- tengo - https://github.com/d5/tengo - tengo - https://github.com/d5/tengo
- keybase - https://github.com/keybase/go-keybase-chat-bot - whatsapp - https://github.com/Rhymen/go-whatsapp/
- msgraph.go - https://github.com/yaegashi/msgraph.go - xmpp - https://github.com/mattn/go-xmpp
- zulip - https://github.com/ifo/gozulipbot
<!-- Links --> <!-- Links -->
[mb-discord]: https://discord.gg/AkKPtrQ
[mb-gitter]: https://gitter.im/42wim/matterbridge [mb-gitter]: https://gitter.im/42wim/matterbridge
[mb-irc]: https://webchat.freenode.net/?channels=matterbridgechat [mb-irc]: https://webchat.freenode.net/?channels=matterbridgechat
[mb-discord]: https://discord.gg/AkKPtrQ [mb-keybase]: https://keybase.io/team/matterbridge
[mb-matrix]: https://riot.im/app/#/room/#matterbridge:matrix.org [mb-matrix]: https://riot.im/app/#/room/#matterbridge:matrix.org
[mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA
[mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e [mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e
[mb-msteams]: https://teams.microsoft.com/join/hj92x75gd3y7
[mb-rocketchat]: https://open.rocket.chat/channel/matterbridge [mb-rocketchat]: https://open.rocket.chat/channel/matterbridge
[mb-xmpp]: https://inverse.chat/ [mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA
[mb-telegram]: https://t.me/Matterbridge
[mb-twitch]: https://www.twitch.tv/matterbridge [mb-twitch]: https://www.twitch.tv/matterbridge
[mb-whatsapp]: https://www.whatsapp.com/ [mb-whatsapp]: https://www.whatsapp.com/
[mb-keybase]: https://keybase.io/team/matterbridge [mb-xmpp]: https://inverse.chat/
[mb-zulip]: https://matterbridge.zulipchat.com/register/ [mb-zulip]: https://matterbridge.zulipchat.com/register/
[mb-telegram]: https://t.me/Matterbridge
[mb-msteams]: https://teams.microsoft.com/join/hj92x75gd3y7

View File

@@ -4,6 +4,7 @@ import (
"log" "log"
"strings" "strings"
"sync" "sync"
"time"
"github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/config"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@@ -74,6 +75,7 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map
for ID, channel := range channels { for ID, channel := range channels {
if !exists[ID] { if !exists[ID] {
b.Log.Infof("%s: joining %s (ID: %s)", b.Account, channel.Name, ID) b.Log.Infof("%s: joining %s (ID: %s)", b.Account, channel.Name, ID)
time.Sleep(time.Duration(b.GetInt("JoinDelay")) * time.Millisecond)
err := b.JoinChannel(channel) err := b.JoinChannel(channel)
if err != nil { if err != nil {
return err return err

View File

@@ -89,6 +89,7 @@ type Protocol struct {
IgnoreNicks string // all protocols IgnoreNicks string // all protocols
IgnoreMessages string // all protocols IgnoreMessages string // all protocols
Jid string // xmpp Jid string // xmpp
JoinDelay string // all protocols
Label string // all protocols Label string // all protocols
Login string // mattermost, matrix Login string // mattermost, matrix
MediaDownloadBlackList []string MediaDownloadBlackList []string

View File

@@ -34,6 +34,8 @@ type Bdiscord struct {
membersMutex sync.RWMutex membersMutex sync.RWMutex
userMemberMap map[string]*discordgo.Member userMemberMap map[string]*discordgo.Member
nickMemberMap map[string]*discordgo.Member nickMemberMap map[string]*discordgo.Member
webhookCache map[string]string
webhookMutex sync.RWMutex
} }
func New(cfg *bridge.Config) bridge.Bridger { func New(cfg *bridge.Config) bridge.Bridger {
@@ -41,6 +43,7 @@ func New(cfg *bridge.Config) bridge.Bridger {
b.userMemberMap = make(map[string]*discordgo.Member) b.userMemberMap = make(map[string]*discordgo.Member)
b.nickMemberMap = make(map[string]*discordgo.Member) b.nickMemberMap = make(map[string]*discordgo.Member)
b.channelInfoMap = make(map[string]*config.ChannelInfo) b.channelInfoMap = make(map[string]*config.ChannelInfo)
b.webhookCache = make(map[string]string)
if b.GetString("WebhookURL") != "" { if b.GetString("WebhookURL") != "" {
b.Log.Debug("Configuring Discord Incoming Webhook") b.Log.Debug("Configuring Discord Incoming Webhook")
b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL")) b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL"))
@@ -188,6 +191,8 @@ func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error {
func (b *Bdiscord) Send(msg config.Message) (string, error) { func (b *Bdiscord) Send(msg config.Message) (string, error) {
b.Log.Debugf("=> Receiving %#v", msg) b.Log.Debugf("=> Receiving %#v", msg)
origMsgID := msg.ID
channelID := b.getChannelID(msg.Channel) channelID := b.getChannelID(msg.Channel)
if channelID == "" { if channelID == "" {
return "", fmt.Errorf("Could not find channelID for %v", msg.Channel) return "", fmt.Errorf("Could not find channelID for %v", msg.Channel)
@@ -230,6 +235,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
// If we are editing a message, delete the old message // If we are editing a message, delete the old message
if msg.ID != "" { if msg.ID != "" {
msg.ID = b.getCacheID(msg.ID)
b.Log.Debugf("Deleting edited webhook message") b.Log.Debugf("Deleting edited webhook message")
err := b.c.ChannelMessageDelete(channelID, msg.ID) err := b.c.ChannelMessageDelete(channelID, msg.ID)
if err != nil { if err != nil {
@@ -273,6 +279,8 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
if msg == nil { if msg == nil {
return "", nil return "", nil
} }
b.updateCacheID(origMsgID, msg.ID)
return msg.ID, nil return msg.ID, nil
} }
@@ -283,6 +291,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
if msg.ID == "" { if msg.ID == "" {
return "", nil return "", nil
} }
msg.ID = b.getCacheID(msg.ID)
err := b.c.ChannelMessageDelete(channelID, msg.ID) err := b.c.ChannelMessageDelete(channelID, msg.ID)
return "", err return "", err
} }

View File

@@ -208,6 +208,40 @@ func (b *Bdiscord) splitURL(url string) (string, string) {
return webhookURLSplit[webhookIdxID], webhookURLSplit[webhookIdxToken] return webhookURLSplit[webhookIdxID], webhookURLSplit[webhookIdxToken]
} }
// getcacheID tries to find a corresponding msgID in the webhook cache.
// if not found returns the original request.
func (b *Bdiscord) getCacheID(msgID string) string {
b.webhookMutex.RLock()
defer b.webhookMutex.RUnlock()
for k, v := range b.webhookCache {
if msgID == k {
return v
}
}
return msgID
}
// updateCacheID updates the cache so that the newID takes the place of
// the original ID. This is used for edit/deletes in combination with webhooks
// as editing a message via webhook means deleting the message and creating a
// new message (with a new ID). This ID needs to be set instead of the original ID
func (b *Bdiscord) updateCacheID(origID, newID string) {
b.webhookMutex.Lock()
match := false
for k, v := range b.webhookCache {
if v == origID {
delete(b.webhookCache, k)
b.webhookCache[origID] = newID
match = true
continue
}
}
if !match && origID != "" {
b.webhookCache[origID] = newID
}
b.webhookMutex.Unlock()
}
func enumerateUsernames(s string) []string { func enumerateUsernames(s string) []string {
onlySpace := true onlySpace := true
for _, r := range s { for _, r := range s {

View File

@@ -54,12 +54,12 @@ func (b *Birc) handleFiles(msg *config.Message) bool {
for _, f := range msg.Extra["file"] { for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo) fi := f.(config.FileInfo)
if fi.Comment != "" { if fi.Comment != "" {
msg.Text += fi.Comment + ": " msg.Text += fi.Comment + " : "
} }
if fi.URL != "" { if fi.URL != "" {
msg.Text = fi.URL msg.Text = fi.URL
if fi.Comment != "" { if fi.Comment != "" {
msg.Text = fi.Comment + ": " + fi.URL msg.Text = fi.Comment + " : " + fi.URL
} }
} }
b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event} b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event}

View File

@@ -71,7 +71,15 @@ func (b *Bmsteams) Disconnect() error {
} }
func (b *Bmsteams) JoinChannel(channel config.ChannelInfo) error { func (b *Bmsteams) JoinChannel(channel config.ChannelInfo) error {
go b.poll(channel.Name) go func(name string) {
for {
err := b.poll(name)
if err != nil {
b.Log.Errorf("polling failed for %s: %s. retrying in 5 seconds", name, err)
}
time.Sleep(time.Second * 5)
}
}(channel.Name)
return nil return nil
} }
@@ -120,12 +128,12 @@ func (b *Bmsteams) getMessages(channel string) ([]msgraph.ChatMessage, error) {
} }
//nolint:gocognit //nolint:gocognit
func (b *Bmsteams) poll(channelName string) { func (b *Bmsteams) poll(channelName string) error {
msgmap := make(map[string]time.Time) msgmap := make(map[string]time.Time)
b.Log.Debug("getting initial messages") b.Log.Debug("getting initial messages")
res, err := b.getMessages(channelName) res, err := b.getMessages(channelName)
if err != nil { if err != nil {
panic(err) return err
} }
for _, msg := range res { for _, msg := range res {
msgmap[*msg.ID] = *msg.CreatedDateTime msgmap[*msg.ID] = *msg.CreatedDateTime
@@ -138,7 +146,7 @@ func (b *Bmsteams) poll(channelName string) {
for { for {
res, err := b.getMessages(channelName) res, err := b.getMessages(channelName)
if err != nil { if err != nil {
panic(err) return err
} }
for i := len(res) - 1; i >= 0; i-- { for i := len(res) - 1; i >= 0; i-- {
msg := res[i] msg := res[i]

View File

@@ -2,6 +2,7 @@ package brocketchat
import ( import (
"github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/config"
"github.com/matterbridge/Rocket.Chat.Go.SDK/models"
) )
func (b *Brocketchat) handleRocket() { func (b *Brocketchat) handleRocket() {
@@ -38,6 +39,23 @@ func (b *Brocketchat) handleRocketHook(messages chan *config.Message) {
} }
} }
func (b *Brocketchat) handleStatusEvent(ev models.Message, rmsg *config.Message) bool {
switch ev.Type {
case "":
// this is a normal message, no processing needed
// return true so the message is not dropped
return true
case sUserJoined, sUserLeft:
rmsg.Event = config.EventJoinLeave
return true
case sRoomChangedTopic:
rmsg.Event = config.EventTopicChange
return true
}
b.Log.Debugf("Dropping message with unknown type: %s", ev.Type)
return false
}
func (b *Brocketchat) handleRocketClient(messages chan *config.Message) { func (b *Brocketchat) handleRocketClient(messages chan *config.Message) {
for message := range b.messageChan { for message := range b.messageChan {
// skip messages with same ID, apparently messages get duplicated for an unknown reason // skip messages with same ID, apparently messages get duplicated for an unknown reason
@@ -59,7 +77,12 @@ func (b *Brocketchat) handleRocketClient(messages chan *config.Message) {
UserID: message.User.ID, UserID: message.User.ID,
ID: message.ID, ID: message.ID,
} }
messages <- rmsg
// handleStatusEvent returns false if the message should be dropped
// in that case it is probably some modification to the channel we do not want to relay
if b.handleStatusEvent(m, rmsg) {
messages <- rmsg
}
} }
} }

View File

@@ -29,6 +29,12 @@ type Brocketchat struct {
sync.RWMutex sync.RWMutex
} }
const (
sUserJoined = "uj"
sUserLeft = "ul"
sRoomChangedTopic = "room_changed_topic"
)
func New(cfg *bridge.Config) bridge.Bridger { func New(cfg *bridge.Config) bridge.Bridger {
newCache, err := lru.New(100) newCache, err := lru.New(100)
if err != nil { if err != nil {
@@ -169,15 +175,6 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) {
rmsg, err := b.c.SendMessage(smsg) rmsg, err := b.c.SendMessage(smsg)
if rmsg == nil { if rmsg == nil {
if strings.Contains(err.Error(), "Invalid or expired session, please login again") ||
strings.Contains(err.Error(), "error-invalid-user") {
b.Log.Info("Login expired, reconnecting..")
err = b.apiLogin()
if err != nil {
return "", err
}
b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EventRejoinChannels}
}
return "", err return "", err
} }
return rmsg.ID, err return rmsg.ID, err

View File

@@ -137,12 +137,6 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
} }
// Skip any messages that we made ourselves or from 'slackbot' (see #527).
if ev.Username == sSlackBotUser ||
(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID {
return true
}
if ev.SubMessage != nil { if ev.SubMessage != nil {
// It seems ev.SubMessage.Edited == nil when slack unfurls. // It seems ev.SubMessage.Edited == nil when slack unfurls.
// Do not forward these messages. See Github issue #266. // Do not forward these messages. See Github issue #266.
@@ -155,6 +149,16 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
if ev.SubType == "message_replied" && ev.Hidden { if ev.SubType == "message_replied" && ev.Hidden {
return true return true
} }
if len(ev.SubMessage.Blocks.BlockSet) == 1 {
block, ok := ev.SubMessage.Blocks.BlockSet[0].(*slack.SectionBlock)
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
}
}
// Skip any messages that we made ourselves or from 'slackbot' (see #527).
if ev.Username == sSlackBotUser ||
(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID {
return true
} }
if len(ev.Files) > 0 { if len(ev.Files) > 0 {

View File

@@ -64,6 +64,7 @@ const (
editSuffixConfig = "EditSuffix" editSuffixConfig = "EditSuffix"
iconURLConfig = "iconurl" iconURLConfig = "iconurl"
noSendJoinConfig = "nosendjoinpart" noSendJoinConfig = "nosendjoinpart"
messageLength = 3000
) )
func New(cfg *bridge.Config) bridge.Bridger { func New(cfg *bridge.Config) bridge.Bridger {
@@ -194,6 +195,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
b.Log.Debugf("=> Receiving %#v", msg) b.Log.Debugf("=> Receiving %#v", msg)
} }
msg.Text = helper.ClipMessage(msg.Text, messageLength)
msg.Text = b.replaceCodeFence(msg.Text) msg.Text = b.replaceCodeFence(msg.Text)
// Make a action /me of the message // Make a action /me of the message

View File

@@ -23,7 +23,8 @@ Check:
// HandleError received from WhatsApp // HandleError received from WhatsApp
func (b *Bwhatsapp) HandleError(err error) { func (b *Bwhatsapp) HandleError(err error) {
// ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843 // ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843
if strings.Contains(err.Error(), "error processing data: received invalid data") { // ignore tag 174 errors. https://github.com/42wim/matterbridge/issues/1094
if strings.Contains(err.Error(), "error processing data: received invalid data") || strings.Contains(err.Error(), "invalid string with tag 174") {
return return
} }

View File

@@ -67,7 +67,7 @@ func (b *Bwhatsapp) Connect() error {
// https://github.com/Rhymen/go-whatsapp#creating-a-connection // https://github.com/Rhymen/go-whatsapp#creating-a-connection
b.Log.Debugln("Connecting to WhatsApp..") b.Log.Debugln("Connecting to WhatsApp..")
conn, err := whatsapp.NewConn(20 * time.Second) conn, err := whatsapp.NewConn(20 * time.Second)
conn.SetClientVersion(0, 4, 1307) conn.SetClientVersion(0, 4, 2080)
if err != nil { if err != nil {
return errors.New("failed to connect to WhatsApp: " + err.Error()) return errors.New("failed to connect to WhatsApp: " + err.Error())
} }

34
bridge/xmpp/handler.go Normal file
View File

@@ -0,0 +1,34 @@
package bxmpp
import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/matterbridge/go-xmpp"
)
// handleDownloadAvatar downloads the avatar of userid from channel
// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
// logs an error message if it fails
func (b *Bxmpp) handleDownloadAvatar(avatar xmpp.AvatarData) {
rmsg := config.Message{
Username: "system",
Text: "avatar",
Channel: b.parseChannel(avatar.From),
Account: b.Account,
UserID: avatar.From,
Event: config.EventAvatarDownload,
Extra: make(map[string][]interface{}),
}
if _, ok := b.avatarMap[avatar.From]; !ok {
b.Log.Debugf("Avatar.From: %s", avatar.From)
err := helper.HandleDownloadSize(b.Log, &rmsg, avatar.From+".png", int64(len(avatar.Data)), b.General)
if err != nil {
b.Log.Error(err)
return
}
helper.HandleDownloadData(b.Log, &rmsg, avatar.From+".png", rmsg.Text, "", &avatar.Data, b.General)
b.Log.Debugf("Avatar download complete")
b.Remote <- rmsg
}
}

30
bridge/xmpp/helpers.go Normal file
View File

@@ -0,0 +1,30 @@
package bxmpp
import (
"regexp"
"github.com/42wim/matterbridge/bridge/config"
)
var pathRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
// GetAvatar constructs a URL for a given user-avatar if it is available in the cache.
func getAvatar(av map[string]string, userid string, general *config.Protocol) string {
if hash, ok := av[userid]; ok {
// NOTE: This does not happen in bridge/helper/helper.go but messes up XMPP
id := pathRegex.ReplaceAllString(userid, "_")
return general.MediaServerDownload + "/" + hash + "/" + id + ".png"
}
return ""
}
func (b *Bxmpp) cacheAvatar(msg *config.Message) string {
fi := msg.Extra["file"][0].(config.FileInfo)
/* if we have a sha we have successfully uploaded the file to the media server,
so we can now cache the sha */
if fi.SHA != "" {
b.Log.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID)
b.avatarMap[msg.UserID] = fi.SHA
}
return ""
}

View File

@@ -23,12 +23,15 @@ type Bxmpp struct {
xmppMap map[string]string xmppMap map[string]string
connected bool connected bool
sync.RWMutex sync.RWMutex
avatarMap map[string]string
} }
func New(cfg *bridge.Config) bridge.Bridger { func New(cfg *bridge.Config) bridge.Bridger {
return &Bxmpp{ return &Bxmpp{
Config: cfg, Config: cfg,
xmppMap: make(map[string]string), xmppMap: make(map[string]string),
avatarMap: make(map[string]string),
} }
} }
@@ -69,6 +72,10 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
} }
b.Log.Debugf("=> Receiving %#v", msg) b.Log.Debugf("=> Receiving %#v", msg)
if msg.Event == config.EventAvatarDownload {
return b.cacheAvatar(&msg), nil
}
// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support). // Upload a file (in XMPP case send the upload URL because XMPP has no native upload support).
if msg.Extra != nil { if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) { for _, rmsg := range helper.HandleExtra(&msg, b.General) {
@@ -230,6 +237,12 @@ func (b *Bxmpp) handleXMPP() error {
event = config.EventTopicChange event = config.EventTopicChange
} }
avatar := getAvatar(b.avatarMap, v.Remote, b.General)
if avatar == "" {
b.Log.Debugf("Requesting avatar data")
b.xc.AvatarRequestData(v.Remote)
}
msgID := v.ID msgID := v.ID
if v.ReplaceID != "" { if v.ReplaceID != "" {
msgID = v.ReplaceID msgID = v.ReplaceID
@@ -239,6 +252,7 @@ func (b *Bxmpp) handleXMPP() error {
Text: v.Text, Text: v.Text,
Channel: b.parseChannel(v.Remote), Channel: b.parseChannel(v.Remote),
Account: b.Account, Account: b.Account,
Avatar: avatar,
UserID: v.Remote, UserID: v.Remote,
ID: msgID, ID: msgID,
Event: event, Event: event,
@@ -255,6 +269,8 @@ func (b *Bxmpp) handleXMPP() error {
b.Log.Debugf("<= Message is %#v", rmsg) b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg b.Remote <- rmsg
} }
case xmpp.AvatarData:
b.handleDownloadAvatar(v)
case xmpp.Presence: case xmpp.Presence:
// Do nothing. // Do nothing.
} }

View File

@@ -1,3 +1,47 @@
# v1.17.4
## Bugfix
- general: Lowercase account names. Fixes #1108 (#1110)
- msteams: Remove panics and retry polling on failure (msteams). Fixes #1104 (#1105
- whatsapp: Update Rhymen/go-whatsapp. Fixes #1107 (#1109) (make whatsapp working again)
- discord: Add an ID cache (discord). Fixes #1106 (#1111) (fix delete/edits with webhooks)
# v1.17.3
## Enhancements
- xmpp: Implement User Avatar spoofing of XMPP users #1090
- rocketchat: Relay Joins/Topic changes in RocketChat bridge (#1085)
- irc: Add JoinDelay option (irc). Fixes #1084 (#1098)
- slack: Clip too long messages on 3000 length (slack). Fixes #1081 (#1102)
## Bugfix
- general: Fix the behavior of ShowTopicChange and SyncTopic (#1086)
- slack: Prevent image/message looping (slack). Fixes #1088 (#1096)
- whatsapp: Ignore non-critical errors (whatsapp). Fixes #1094 (#1100)
- irc: Add extra space before colon in attachments (irc). Fixes #1089 (#1101)
This release couldn't exist without the following contributors:
@42wim, @ldruschk, @qaisjp, @Polynomdivision
# v1.17.2
## Enhancements
- slack: Update vendor slack-go/slack (#1068)
- general: Update vendor d5/tengo (#1066)
- general: Clarify terminology used in mapping group chat IDs to channels in config (#1079)
## Bugfix
- whatsapp: Update Rhymen/go-whatsapp vendor and whatsapp version (#1078). Fixes Media upload #1074
- whatsapp: Reset start timestamp on reconnect (whatsapp). Fixes #1059 (#1064)
This release couldn't exist without the following contributors:
@42wim, @jheiselman
# v1.17.1 # v1.17.1
## Enhancements ## Enhancements

View File

@@ -108,7 +108,7 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
func (gw *Gateway) checkConfig(cfg *config.Bridge) { func (gw *Gateway) checkConfig(cfg *config.Bridge) {
match := false match := false
for _, key := range gw.Router.Config.Viper().AllKeys() { for _, key := range gw.Router.Config.Viper().AllKeys() {
if strings.HasPrefix(key, cfg.Account) { if strings.HasPrefix(key, strings.ToLower(cfg.Account)) {
match = true match = true
break break
} }

View File

@@ -169,7 +169,7 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
switch event { switch event {
case config.EventAvatarDownload: case config.EventAvatarDownload:
// Avatar downloads are only relevant for telegram and mattermost for now // Avatar downloads are only relevant for telegram and mattermost for now
if dest.Protocol != "mattermost" && dest.Protocol != "telegram" { if dest.Protocol != "mattermost" && dest.Protocol != "telegram" && dest.Protocol != "xmpp" {
return true return true
} }
case config.EventJoinLeave: case config.EventJoinLeave:
@@ -179,7 +179,7 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
} }
case config.EventTopicChange: case config.EventTopicChange:
// only relay topic change when used in some way on other side // only relay topic change when used in some way on other side
if dest.GetBool("ShowTopicChange") && dest.GetBool("SyncTopic") { if !dest.GetBool("ShowTopicChange") && !dest.GetBool("SyncTopic") {
return true return true
} }
} }

6
go.mod
View File

@@ -5,7 +5,7 @@ require (
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
github.com/Jeffail/gabs v1.1.1 // indirect github.com/Jeffail/gabs v1.1.1 // indirect
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0
github.com/Rhymen/go-whatsapp v0.1.0 github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334
github.com/d5/tengo/v2 v2.1.2 github.com/d5/tengo/v2 v2.1.2
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
github.com/fsnotify/fsnotify v1.4.7 github.com/fsnotify/fsnotify v1.4.7
@@ -21,10 +21,10 @@ require (
github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e
github.com/labstack/echo/v4 v4.1.13 github.com/labstack/echo/v4 v4.1.13
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible
github.com/matterbridge/go-xmpp v0.0.0-20200329150250-5812999b292b github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6 github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61 github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61

12
go.sum
View File

@@ -12,8 +12,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 h1:TO7d4rocnNFng6ZQrPe7U6WqHtK5eHEMrgrnnM/72IQ= github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 h1:TO7d4rocnNFng6ZQrPe7U6WqHtK5eHEMrgrnnM/72IQ=
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg= github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
github.com/Rhymen/go-whatsapp v0.1.0 h1:XTXhFIQ/fx9jKObUnUX2Q+nh58EyeHNhX7DniE8xeuA= github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334 h1:kb1zvD+xd+XbPUdQ0lMxnRaQ76N5C9vMAClLi8Dyw1Y=
github.com/Rhymen/go-whatsapp v0.1.0/go.mod h1:xJSy+okeRjKkQEH/lEYrnekXB3PG33fqL0I6ncAkV50= github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk=
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME= github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME=
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU= github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU=
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw= github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw=
@@ -124,14 +124,14 @@ github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 h1:BS9tqL0OCiOGuy/C
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7/go.mod h1:liX5MxHPrwgHaKowoLkYGwbXfYABh1jbZ6FpElbGF1I= github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7/go.mod h1:liX5MxHPrwgHaKowoLkYGwbXfYABh1jbZ6FpElbGF1I=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d h1:F+Sr+C0ojSlYQ37BLylQtSFmyQULe3jbAygcyXQ9mVs= github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048 h1:B9HaistmV+MD8/33BXmZe1zPIn+RImAFVXNNSOrwU2E=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A= github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 h1:VP/DNRn2HtrVRN6+X3h4FDcQI2OOKT+88WUi21ZD1Kw= github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 h1:VP/DNRn2HtrVRN6+X3h4FDcQI2OOKT+88WUi21ZD1Kw=
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3/go.mod h1:5a1bHtG/38ofcx9cgwM5eTW/Pl4SpbQksNDnTRcGA2Y= github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3/go.mod h1:5a1bHtG/38ofcx9cgwM5eTW/Pl4SpbQksNDnTRcGA2Y=
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible h1:oaOqwbg5HxHRxvAbd84ks0Okwoc1ISyUZ87EiVJFhGI= github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible h1:oaOqwbg5HxHRxvAbd84ks0Okwoc1ISyUZ87EiVJFhGI=
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible/go.mod h1:igE6rUAn3jai2wCdsjFHfhUoekjrFthoEjFObKKwSb4= github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible/go.mod h1:igE6rUAn3jai2wCdsjFHfhUoekjrFthoEjFObKKwSb4=
github.com/matterbridge/go-xmpp v0.0.0-20200329150250-5812999b292b h1:ZYI2HCj9zPzI4Si1ouSOi/ImA2xSQLUCJPQsLWr8FE0= github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050 h1:kWkP1lXpkvtoNL08jkP3XQH/zvDOEXJpdCJd/DlIvMw=
github.com/matterbridge/go-xmpp v0.0.0-20200329150250-5812999b292b/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q= github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q=
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6 h1:Kl65VJv38HjYFnnwH+MP6Z8hcJT5UHuSpHVU5vW1HH0= github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6 h1:Kl65VJv38HjYFnnwH+MP6Z8hcJT5UHuSpHVU5vW1HH0=
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g= github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g=
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtWGtfTgZVxHG1lcKjv+re7dRwyyuYFNu69xdho= github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtWGtfTgZVxHG1lcKjv+re7dRwyyuYFNu69xdho=

View File

@@ -15,7 +15,7 @@ import (
) )
var ( var (
version = "1.17.2-dev" version = "1.17.4"
githash string githash string
flagConfig = flag.String("conf", "matterbridge.toml", "config file") flagConfig = flag.String("conf", "matterbridge.toml", "config file")

View File

@@ -177,6 +177,12 @@ StripNick=false
#OPTIONAL (default false) #OPTIONAL (default false)
ShowTopicChange=false ShowTopicChange=false
#Delay in milliseconds between channel joins
#Only useful when you have a LOT of channels to join
#See https://github.com/42wim/matterbridge/issues/1084
#OPTIONAL (default 0)
JoinDelay=0
################################################################### ###################################################################
#XMPP section #XMPP section
################################################################### ###################################################################
@@ -1679,34 +1685,44 @@ enable=true
# REQUIRED # REQUIRED
account="irc.freenode" account="irc.freenode"
# channel to connect on that account # The channel key in each gateway is mapped to a similar group chat ID on the chat platform
# How to specify them for the different bridges: # To find the group chat ID for different platforms, refer to the table below
# #
# irc - #channel (# is required) (this needs to be lowercase!) # Platform | Identifier name | Example | Description
# mattermost - channel (the channel name as seen in the URL, not the displayname) # -------------------------------------------------------------------------------------------------------------------------------------
# gitter - username/room # | channel | general | Do not include the # symbol
# xmpp - channel # discord | channel id | ID:123456789 | See https://github.com/42wim/matterbridge/issues/57
# slack - channel (without the #) # | category/channel | Media/gaming | Without # symbol. If you're using discord categories to group your channels
# - ID:C123456 (where C123456 is the channel ID) does not work with webhook # -------------------------------------------------------------------------------------------------------------------------------------
# discord - channel (without the #) # gitter | username/room | general | As seen in the gitter.im URL
# - ID:123456789 (where 123456789 is the channel ID) # -------------------------------------------------------------------------------------------------------------------------------------
# (https://github.com/42wim/matterbridge/issues/57) # hipchat | id_channel | example needed | See https://www.hipchat.com/account/xmpp for the correct channel
# - category/channel (without the #) if you're using discord categories to group your channels # -------------------------------------------------------------------------------------------------------------------------------------
# telegram - chatid (a large negative number, eg -123456789) # irc | channel | #general | The # symbol is required and should be lowercase!
# see (https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau) # -------------------------------------------------------------------------------------------------------------------------------------
# hipchat - id_channel (see https://www.hipchat.com/account/xmpp for the correct channel) # mattermost | channel | general | This is the channel name as seen in the URL, not the display name
# rocketchat - #channel (# is required (also needed for private channels!) # -------------------------------------------------------------------------------------------------------------------------------------
# matrix - #channel:server (eg #yourchannel:matrix.org) # matrix | #channel:server | #yourchannel:matrix.org | Encrypted rooms are not supported in matrix
# - encrypted rooms are not supported in matrix # -------------------------------------------------------------------------------------------------------------------------------------
# msteams - 19:xxxxxxxxxxxxxxxxxxxxxxxxxx@thread.skype # msteams | threadId | 19:82abcxx@thread.skype | You'll find the threadId in the URL
# - You'll find the channel ID in the URL in the threadId=19:82abcxxxxxxxxx@thread.skype # -------------------------------------------------------------------------------------------------------------------------------------
# steam - chatid (a large number). # rocketchat | channel | #channel | # is required for private channels too
# The number in the URL when you click "enter chat room" in the browser # -------------------------------------------------------------------------------------------------------------------------------------
# whatsapp - 48111222333-123455678999@g.us A unique group JID; # slack | channel name | general | Do not include the # symbol
# if you specify an empty string bridge will list all the possibilities # | channel id | ID:C123456 | The underlying ID of a channel. This doesn't work with
# - "Group Name" if you specify a group name the bridge will hint its JID to specify # -------------------------------------------------------------------------------------------------------------------------------------
# as group names might change in time and contain weird emoticons # steam | chatid | example needed | The number in the URL when you click "enter chat room" in the browser
# zulip - stream/topic:topicname (without the #) # -------------------------------------------------------------------------------------------------------------------------------------
# telegram | chatid | -123456789 | A large negative number. see https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau
# -------------------------------------------------------------------------------------------------------------------------------------
# whatsapp | group JID | 48111222333-123455678999@g.us | A unique group JID. If you specify an empty string, bridge will list all the possibilities
# | "Group Name" | "Family Chat" | if you specify a group name, the bridge will find hint the JID to specify. Names can change over time and are not stable.
# -------------------------------------------------------------------------------------------------------------------------------------
# xmpp | channel | general | The room name
# -------------------------------------------------------------------------------------------------------------------------------------
# zulip | stream/topic:topic | general/off-topic:food | Do not use the # when specifying a topic
# -------------------------------------------------------------------------------------------------------------------------------------
# #
# REQUIRED # REQUIRED
channel="#testing" channel="#testing"

File diff suppressed because it is too large Load Diff

View File

@@ -56,6 +56,8 @@ message Location {
} }
message Point { message Point {
optional int32 xDeprecated = 1;
optional int32 yDeprecated = 2;
optional double x = 3; optional double x = 3;
optional double y = 4; optional double y = 4;
} }
@@ -93,6 +95,7 @@ message ContextInfo {
optional AdReplyInfo quotedAd = 23; optional AdReplyInfo quotedAd = 23;
optional MessageKey placeholderKey = 24; optional MessageKey placeholderKey = 24;
optional uint32 expiration = 25; optional uint32 expiration = 25;
optional int64 ephemeralSettingTimestamp = 26;
} }
message SenderKeyDistributionMessage { message SenderKeyDistributionMessage {
@@ -136,6 +139,11 @@ message LocationMessage {
optional string name = 3; optional string name = 3;
optional string address = 4; optional string address = 4;
optional string url = 5; optional string url = 5;
optional bool isLive = 6;
optional uint32 accuracyInMeters = 7;
optional float speedInMps = 8;
optional uint32 degreesClockwiseFromMagneticNorth = 9;
optional string comment = 11;
optional bytes jpegThumbnail = 16; optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17; optional ContextInfo contextInfo = 17;
} }
@@ -238,9 +246,29 @@ message ProtocolMessage {
enum PROTOCOL_MESSAGE_TYPE { enum PROTOCOL_MESSAGE_TYPE {
REVOKE = 0; REVOKE = 0;
EPHEMERAL_SETTING = 3; EPHEMERAL_SETTING = 3;
EPHEMERAL_SYNC_RESPONSE = 4;
HISTORY_SYNC_NOTIFICATION = 5;
} }
optional PROTOCOL_MESSAGE_TYPE type = 2; optional PROTOCOL_MESSAGE_TYPE type = 2;
optional uint32 ephemeralExpiration = 4; optional uint32 ephemeralExpiration = 4;
optional int64 ephemeralSettingTimestamp = 5;
optional HistorySyncNotification historySyncNotification = 6;
}
message HistorySyncNotification {
optional bytes fileSha256 = 1;
optional uint64 fileLength = 2;
optional bytes mediaKey = 3;
optional bytes fileEncSha256 = 4;
optional string directPath = 5;
enum HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE {
INITIAL_BOOTSTRAP = 0;
INITIAL_STATUS_V3 = 1;
FULL = 2;
RECENT = 3;
}
optional HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE syncType = 6;
optional uint32 chunkOrder = 7;
} }
message ContactsArrayMessage { message ContactsArrayMessage {
@@ -355,6 +383,8 @@ message StickerMessage {
optional int64 mediaKeyTimestamp = 10; optional int64 mediaKeyTimestamp = 10;
optional uint32 firstFrameLength = 11; optional uint32 firstFrameLength = 11;
optional bytes firstFrameSidecar = 12; optional bytes firstFrameSidecar = 12;
optional bool isAnimated = 13;
optional bytes pngThumbnail = 16;
optional ContextInfo contextInfo = 17; optional ContextInfo contextInfo = 17;
} }
@@ -401,6 +431,12 @@ message TemplateButtonReplyMessage {
optional uint32 selectedIndex = 4; optional uint32 selectedIndex = 4;
} }
message CatalogSnapshot {
optional ImageMessage catalogImage = 1;
optional string title = 2;
optional string description = 3;
}
message ProductSnapshot { message ProductSnapshot {
optional ImageMessage productImage = 1; optional ImageMessage productImage = 1;
optional string productId = 2; optional string productId = 2;
@@ -417,6 +453,7 @@ message ProductSnapshot {
message ProductMessage { message ProductMessage {
optional ProductSnapshot product = 1; optional ProductSnapshot product = 1;
optional string businessOwnerJid = 2; optional string businessOwnerJid = 2;
optional CatalogSnapshot catalog = 4;
optional ContextInfo contextInfo = 17; optional ContextInfo contextInfo = 17;
} }
@@ -513,6 +550,8 @@ message WebFeatures {
optional WEB_FEATURES_FLAG templateMessage = 30; optional WEB_FEATURES_FLAG templateMessage = 30;
optional WEB_FEATURES_FLAG templateMessageInteractivity = 31; optional WEB_FEATURES_FLAG templateMessageInteractivity = 31;
optional WEB_FEATURES_FLAG ephemeralMessages = 32; optional WEB_FEATURES_FLAG ephemeralMessages = 32;
optional WEB_FEATURES_FLAG e2ENotificationSync = 33;
optional WEB_FEATURES_FLAG recentStickersV2 = 34;
} }
message TabletNotificationsInfo { message TabletNotificationsInfo {
@@ -537,6 +576,11 @@ message WebNotificationsInfo {
} }
message PaymentInfo { message PaymentInfo {
enum PAYMENT_INFO_CURRENCY {
UNKNOWN_CURRENCY = 0;
INR = 1;
}
optional PAYMENT_INFO_CURRENCY currencyDeprecated = 1;
optional uint64 amount1000 = 2; optional uint64 amount1000 = 2;
optional string receiverJid = 3; optional string receiverJid = 3;
enum PAYMENT_INFO_STATUS { enum PAYMENT_INFO_STATUS {
@@ -559,6 +603,37 @@ message PaymentInfo {
optional uint64 expiryTimestamp = 7; optional uint64 expiryTimestamp = 7;
optional bool futureproofed = 8; optional bool futureproofed = 8;
optional string currency = 9; optional string currency = 9;
enum PAYMENT_INFO_TXNSTATUS {
UNKNOWN = 0;
PENDING_SETUP = 1;
PENDING_RECEIVER_SETUP = 2;
INIT = 3;
SUCCESS = 4;
COMPLETED = 5;
FAILED = 6;
FAILED_RISK = 7;
FAILED_PROCESSING = 8;
FAILED_RECEIVER_PROCESSING = 9;
FAILED_DA = 10;
FAILED_DA_FINAL = 11;
REFUNDED_TXN = 12;
REFUND_FAILED = 13;
REFUND_FAILED_PROCESSING = 14;
REFUND_FAILED_DA = 15;
EXPIRED_TXN = 16;
AUTH_CANCELED = 17;
AUTH_CANCEL_FAILED_PROCESSING = 18;
AUTH_CANCEL_FAILED = 19;
COLLECT_INIT = 20;
COLLECT_SUCCESS = 21;
COLLECT_FAILED = 22;
COLLECT_FAILED_RISK = 23;
COLLECT_REJECTED = 24;
COLLECT_EXPIRED = 25;
COLLECT_CANCELED = 26;
COLLECT_CANCELLING = 27;
}
optional PAYMENT_INFO_TXNSTATUS txnStatus = 10;
} }
message WebMessageInfo { message WebMessageInfo {
@@ -668,4 +743,5 @@ message WebMessageInfo {
optional PaymentInfo quotedPaymentInfo = 31; optional PaymentInfo quotedPaymentInfo = 31;
optional uint64 ephemeralStartTimestamp = 32; optional uint64 ephemeralStartTimestamp = 32;
optional uint32 ephemeralDuration = 33; optional uint32 ephemeralDuration = 33;
} }

View File

@@ -90,6 +90,7 @@ type Conn struct {
longClientName string longClientName string
shortClientName string shortClientName string
clientVersion string
loginSessionLock sync.RWMutex loginSessionLock sync.RWMutex
Proxy func(*http.Request) (*url.URL, error) Proxy func(*http.Request) (*url.URL, error)
@@ -121,6 +122,7 @@ func NewConn(timeout time.Duration) (*Conn, error) {
longClientName: "github.com/rhymen/go-whatsapp", longClientName: "github.com/rhymen/go-whatsapp",
shortClientName: "go-whatsapp", shortClientName: "go-whatsapp",
clientVersion: "0.1.0",
} }
return wac, wac.connect() return wac, wac.connect()
} }
@@ -135,6 +137,7 @@ func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL
longClientName: "github.com/rhymen/go-whatsapp", longClientName: "github.com/rhymen/go-whatsapp",
shortClientName: "go-whatsapp", shortClientName: "go-whatsapp",
clientVersion: "0.1.0",
Proxy: proxy, Proxy: proxy,
} }
return wac, wac.connect() return wac, wac.connect()

View File

@@ -6,7 +6,7 @@ require (
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d // indirect github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d // indirect
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d // indirect github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d // indirect
github.com/golang/protobuf v1.3.0 github.com/golang/protobuf v1.3.0
github.com/gorilla/websocket v1.4.0 github.com/gorilla/websocket v1.4.1
github.com/pkg/errors v0.8.1 github.com/pkg/errors v0.8.1
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
) )

View File

@@ -12,8 +12,9 @@ github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=

View File

@@ -10,10 +10,8 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"mime/multipart"
"net/http" "net/http"
"os" "net/url"
"strings"
"time" "time"
"github.com/Rhymen/go-whatsapp/crypto/cbc" "github.com/Rhymen/go-whatsapp/crypto/cbc"
@@ -95,7 +93,50 @@ func downloadMedia(url string) (file []byte, mac []byte, err error) {
return data[:n-10], data[n-10 : n], nil return data[:n-10], data[n-10 : n], nil
} }
func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) { type MediaConn struct {
Status int `json:"status"`
MediaConn struct {
Auth string `json:"auth"`
TTL int `json:"ttl"`
Hosts []struct {
Hostname string `json:"hostname"`
IPs []string `json:"ips"`
} `json:"hosts"`
} `json:"media_conn"`
}
func (wac *Conn) queryMediaConn() (hostname, auth string, ttl int, err error) {
queryReq := []interface{}{"query", "mediaConn"}
ch, err := wac.writeJson(queryReq)
if err != nil {
return "", "", 0, err
}
var resp MediaConn
select {
case r := <-ch:
if err = json.Unmarshal([]byte(r), &resp); err != nil {
return "", "", 0, fmt.Errorf("error decoding query media conn response: %v", err)
}
case <-time.After(wac.msgTimeout):
return "", "", 0, fmt.Errorf("query media conn timed out")
}
if resp.Status != 200 {
return "", "", 0, fmt.Errorf("query media conn responded with %d", resp.Status)
}
return resp.MediaConn.Hosts[0].Hostname, resp.MediaConn.Auth, resp.MediaConn.TTL, nil
}
var mediaTypeMap = map[MediaType]string{
MediaImage: "/mms/image",
MediaVideo: "/mms/video",
MediaDocument: "/mms/document",
MediaAudio: "/mms/audio",
}
func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) {
data, err := ioutil.ReadAll(reader) data, err := ioutil.ReadAll(reader)
if err != nil { if err != nil {
return "", nil, nil, nil, 0, err return "", nil, nil, nil, 0, err
@@ -128,67 +169,30 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaK
sha.Write(append(enc, mac...)) sha.Write(append(enc, mac...))
fileEncSha256 = sha.Sum(nil) fileEncSha256 = sha.Sum(nil)
var filetype string hostname, auth, _, err := wac.queryMediaConn()
switch appInfo { token := base64.URLEncoding.EncodeToString(fileEncSha256)
case MediaImage: q := url.Values{
filetype = "image" "auth": []string{auth},
case MediaAudio: "token": []string{token},
filetype = "audio" }
case MediaDocument: path := mediaTypeMap[appInfo]
filetype = "document" uploadURL := url.URL{
case MediaVideo: Scheme: "https",
filetype = "video" Host: hostname,
Path: fmt.Sprintf("%s/%s", path, token),
RawQuery: q.Encode(),
} }
uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)} body := bytes.NewReader(append(enc, mac...))
ch, err := wac.writeJson(uploadReq)
req, err := http.NewRequest("POST", uploadURL.String(), body)
if err != nil { if err != nil {
return "", nil, nil, nil, 0, err return "", nil, nil, nil, 0, err
} }
var resp map[string]interface{}
select {
case r := <-ch:
if err = json.Unmarshal([]byte(r), &resp); err != nil {
return "", nil, nil, nil, 0, fmt.Errorf("error decoding upload response: %v", err)
}
case <-time.After(wac.msgTimeout):
return "", nil, nil, nil, 0, fmt.Errorf("restore session init timed out")
}
if int(resp["status"].(float64)) != 200 {
return "", nil, nil, nil, 0, fmt.Errorf("upload responsed with %d", resp["status"])
}
var b bytes.Buffer
w := multipart.NewWriter(&b)
hashWriter, err := w.CreateFormField("hash")
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
io.Copy(hashWriter, strings.NewReader(base64.StdEncoding.EncodeToString(fileEncSha256)))
fileWriter, err := w.CreateFormFile("file", "blob")
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
io.Copy(fileWriter, bytes.NewReader(append(enc, mac...)))
err = w.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
req, err := http.NewRequest("POST", resp["url"].(string), &b)
if err != nil {
return "", nil, nil, nil, 0, err
}
req.Header.Set("Content-Type", w.FormDataContentType())
req.Header.Set("Origin", "https://web.whatsapp.com") req.Header.Set("Origin", "https://web.whatsapp.com")
req.Header.Set("Referer", "https://web.whatsapp.com/") req.Header.Set("Referer", "https://web.whatsapp.com/")
req.URL.Query().Set("f", "j")
client := &http.Client{} client := &http.Client{}
// Submit the request // Submit the request
res, err := client.Do(req) res, err := client.Do(req)

View File

@@ -15,7 +15,10 @@ import (
) )
func (wac *Conn) readPump() { func (wac *Conn) readPump() {
defer wac.wg.Done() defer func() {
wac.wg.Done()
_, _ = wac.Disconnect()
}()
var readErr error var readErr error
var msgType int var msgType int
@@ -31,7 +34,6 @@ func (wac *Conn) readPump() {
case <-readerFound: case <-readerFound:
if readErr != nil { if readErr != nil {
wac.handle(&ErrConnectionFailed{Err: readErr}) wac.handle(&ErrConnectionFailed{Err: readErr})
_, _ = wac.Disconnect()
return return
} }
msg, err := ioutil.ReadAll(reader) msg, err := ioutil.ReadAll(reader)

View File

@@ -18,7 +18,7 @@ import (
) )
//represents the WhatsAppWeb client version //represents the WhatsAppWeb client version
var waVersion = []int{0, 3, 3324} var waVersion = []int{0, 4, 2080}
/* /*
Session contains session individual information. To be able to resume the connection without scanning the qr code Session contains session individual information. To be able to resume the connection without scanning the qr code
@@ -107,10 +107,10 @@ func CheckCurrentServerVersion() ([]int, error) {
} }
b64ClientId := base64.StdEncoding.EncodeToString(clientId) b64ClientId := base64.StdEncoding.EncodeToString(clientId)
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, b64ClientId, true} login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, b64ClientId, true}
loginChan, err := wac.writeJson(login) loginChan, err := wac.writeJson(login)
if err != nil { if err != nil {
return nil, fmt.Errorf("error writing login", err) return nil, fmt.Errorf("error writing login: %s", err.Error())
} }
// Retrieve an answer from the websocket // Retrieve an answer from the websocket
@@ -123,7 +123,7 @@ func CheckCurrentServerVersion() ([]int, error) {
var resp map[string]interface{} var resp map[string]interface{}
if err = json.Unmarshal([]byte(r), &resp); err != nil { if err = json.Unmarshal([]byte(r), &resp); err != nil {
return nil, fmt.Errorf("error decoding login", err) return nil, fmt.Errorf("error decoding login: %s", err.Error())
} }
// Take the curr property as X.Y.Z and split it into as int slice // Take the curr property as X.Y.Z and split it into as int slice
@@ -141,17 +141,17 @@ func CheckCurrentServerVersion() ([]int, error) {
SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the
WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible. WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible.
*/ */
func (wac *Conn) SetClientName(long, short string) error { func (wac *Conn) SetClientName(long, short, version string) error {
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) { if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
return fmt.Errorf("cannot change client name after logging in") return fmt.Errorf("cannot change client name after logging in")
} }
wac.longClientName, wac.shortClientName = long, short wac.longClientName, wac.shortClientName, wac.clientVersion = long, short, version
return nil return nil
} }
/* /*
SetClientVersion sets WhatsApp client version SetClientVersion sets WhatsApp client version
Default value is 0.3.3324 Default value is 0.4.2080
*/ */
func (wac *Conn) SetClientVersion(major int, minor int, patch int) { func (wac *Conn) SetClientVersion(major int, minor int, patch int) {
waVersion = []int{major, minor, patch} waVersion = []int{major, minor, patch}
@@ -213,7 +213,7 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
} }
session.ClientId = base64.StdEncoding.EncodeToString(clientId) session.ClientId = base64.StdEncoding.EncodeToString(clientId)
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true} login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, session.ClientId, true}
loginChan, err := wac.writeJson(login) loginChan, err := wac.writeJson(login)
if err != nil { if err != nil {
return session, fmt.Errorf("error writing login: %v\n", err) return session, fmt.Errorf("error writing login: %v\n", err)
@@ -369,7 +369,7 @@ func (wac *Conn) Restore() error {
wac.listener.Unlock() wac.listener.Unlock()
//admin init //admin init
init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, wac.session.ClientId, true} init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, wac.session.ClientId, true}
initChan, err := wac.writeJson(init) initChan, err := wac.writeJson(init)
if err != nil { if err != nil {
return fmt.Errorf("error writing admin init: %v\n", err) return fmt.Errorf("error writing admin init: %v\n", err)

View File

@@ -7,6 +7,7 @@ type Message struct {
RoomID string `json:"rid"` RoomID string `json:"rid"`
Msg string `json:"msg"` Msg string `json:"msg"`
EditedBy string `json:"editedBy,omitempty"` EditedBy string `json:"editedBy,omitempty"`
Type string `json:"t,omitempty"`
Groupable bool `json:"groupable,omitempty"` Groupable bool `json:"groupable,omitempty"`
@@ -16,6 +17,9 @@ type Message struct {
Mentions []User `json:"mentions,omitempty"` Mentions []User `json:"mentions,omitempty"`
User *User `json:"u,omitempty"` User *User `json:"u,omitempty"`
Attachments []Attachment `json:"attachments,omitempty"`
PostMessage PostMessage
// Bot interface{} `json:"bot"` // Bot interface{} `json:"bot"`

View File

@@ -191,6 +191,26 @@ func getMessageFromData(data interface{}) *models.Message {
func getMessageFromDocument(arg *gabs.Container) *models.Message { func getMessageFromDocument(arg *gabs.Container) *models.Message {
var ts *time.Time var ts *time.Time
var attachments []models.Attachment
attachmentSrc, err := arg.Path("attachments").Children()
if err != nil {
attachments = make([]models.Attachment, 0)
} else {
attachments = make([]models.Attachment, len(attachmentSrc))
for i, attachment := range attachmentSrc {
attachments[i] = models.Attachment{
Timestamp: stringOrZero(attachment.Path("ts").Data()),
Title: stringOrZero(attachment.Path("title").Data()),
TitleLink: stringOrZero(attachment.Path("title_link").Data()),
TitleLinkDownload: stringOrZero(attachment.Path("title_link_download").Data()),
ImageURL: stringOrZero(attachment.Path("image_url").Data()),
AuthorName: stringOrZero(arg.Path("u.name").Data()),
}
}
}
date := stringOrZero(arg.Path("ts.$date").Data()) date := stringOrZero(arg.Path("ts.$date").Data())
if len(date) > 0 { if len(date) > 0 {
if ti, err := strconv.ParseFloat(date, 64); err == nil { if ti, err := strconv.ParseFloat(date, 64); err == nil {
@@ -202,11 +222,13 @@ func getMessageFromDocument(arg *gabs.Container) *models.Message {
ID: stringOrZero(arg.Path("_id").Data()), ID: stringOrZero(arg.Path("_id").Data()),
RoomID: stringOrZero(arg.Path("rid").Data()), RoomID: stringOrZero(arg.Path("rid").Data()),
Msg: stringOrZero(arg.Path("msg").Data()), Msg: stringOrZero(arg.Path("msg").Data()),
Type: stringOrZero(arg.Path("t").Data()),
Timestamp: ts, Timestamp: ts,
User: &models.User{ User: &models.User{
ID: stringOrZero(arg.Path("u._id").Data()), ID: stringOrZero(arg.Path("u._id").Data()),
UserName: stringOrZero(arg.Path("u.username").Data()), UserName: stringOrZero(arg.Path("u.username").Data()),
}, },
Attachments: attachments,
} }
} }

View File

@@ -713,85 +713,119 @@ func (c *Client) Recv() (stanza interface{}, err error) {
Errors: errsStr, Errors: errsStr,
}, nil }, nil
} }
case v.Type == "result" && v.ID == "unsub1": case v.Type == "result":
// Unsubscribing MAY contain a pubsub element. But it does
// not have to
return PubsubUnsubscription{
SubID: "",
JID: v.From,
Node: "",
Errors: nil,
}, nil
case v.Query.XMLName.Local == "pubsub":
switch v.ID { switch v.ID {
case "sub1": case "sub1":
// Subscription or unsubscription was successful if v.Query.XMLName.Local == "pubsub" {
var sub clientPubsubSubscription // Subscription or unsubscription was successful
err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub) var sub clientPubsubSubscription
if err != nil { err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
return PubsubSubscription{}, err if err != nil {
} return PubsubSubscription{}, err
}
return PubsubSubscription{ return PubsubSubscription{
SubID: sub.SubID, SubID: sub.SubID,
JID: sub.JID, JID: sub.JID,
Node: sub.Node, Node: sub.Node,
Errors: nil, Errors: nil,
}, nil
case "unsub1":
var sub clientPubsubSubscription
err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
if err != nil {
return PubsubUnsubscription{}, err
}
return PubsubUnsubscription{
SubID: sub.SubID,
JID: v.From,
Node: sub.Node,
Errors: nil,
}, nil
case "items1", "items3":
var p clientPubsubItems
err := xml.Unmarshal([]byte(v.Query.InnerXML), &p)
if err != nil {
return PubsubItems{}, err
}
switch p.Node {
case XMPPNS_AVATAR_PEP_DATA:
return handleAvatarData(p.Items[0].Body,
v.From,
p.Items[0].ID)
case XMPPNS_AVATAR_PEP_METADATA:
return handleAvatarMetadata(p.Items[0].Body,
v.From)
default:
return PubsubItems{
p.Node,
pubsubItemsToReturn(p.Items),
}, nil }, nil
} }
case "unsub1":
if v.Query.XMLName.Local == "pubsub" {
var sub clientPubsubSubscription
err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
if err != nil {
return PubsubUnsubscription{}, err
}
return PubsubUnsubscription{
SubID: sub.SubID,
JID: v.From,
Node: sub.Node,
Errors: nil,
}, nil
} else {
// Unsubscribing MAY contain a pubsub element. But it does
// not have to
return PubsubUnsubscription{
SubID: "",
JID: v.From,
Node: "",
Errors: nil,
}, nil
}
case "info1":
if v.Query.XMLName.Space == XMPPNS_DISCO_ITEMS {
var itemsQuery clientDiscoItemsQuery
err := xml.Unmarshal(v.InnerXML, &itemsQuery)
if err != nil {
return []DiscoItem{}, err
}
return DiscoItems{
Jid: v.From,
Items: clientDiscoItemsToReturn(itemsQuery.Items),
}, nil
}
case "info3":
if v.Query.XMLName.Space == XMPPNS_DISCO_INFO {
var disco clientDiscoQuery
err := xml.Unmarshal(v.InnerXML, &disco)
if err != nil {
return DiscoResult{}, err
}
return DiscoResult{
Features: clientFeaturesToReturn(disco.Features),
Identities: clientIdentitiesToReturn(disco.Identities),
}, nil
}
case "items1", "items3":
if v.Query.XMLName.Local == "pubsub" {
var p clientPubsubItems
err := xml.Unmarshal([]byte(v.Query.InnerXML), &p)
if err != nil {
return PubsubItems{}, err
}
switch p.Node {
case XMPPNS_AVATAR_PEP_DATA:
return handleAvatarData(p.Items[0].Body,
v.From,
p.Items[0].ID)
case XMPPNS_AVATAR_PEP_METADATA:
return handleAvatarMetadata(p.Items[0].Body,
v.From)
default:
return PubsubItems{
p.Node,
pubsubItemsToReturn(p.Items),
}, nil
}
}
// Note: XEP-0084 states that metadata and data // Note: XEP-0084 states that metadata and data
// should be fetched with an id of retrieve1. // should be fetched with an id of retrieve1.
// Since we already have PubSub implemented, we // Since we already have PubSub implemented, we
// can just use items1 and items3 to do the same // can just use items1 and items3 to do the same
// as an Avatar node is just a PEP (PubSub) node. // as an Avatar node is just a PEP (PubSub) node.
/*case "retrieve1": /*case "retrieve1":
var p clientPubsubItems if v.Query.XMLName.Local == "pubsub" {
err := xml.Unmarshal([]byte(v.Query.InnerXML), &p) var p clientPubsubItems
if err != nil { err := xml.Unmarshal([]byte(v.Query.InnerXML), &p)
return PubsubItems{}, err if err != nil {
} return PubsubItems{}, err
}
switch p.Node { switch p.Node {
case XMPPNS_AVATAR_PEP_DATA: case XMPPNS_AVATAR_PEP_DATA:
return handleAvatarData(p.Items[0].Body, return handleAvatarData(p.Items[0].Body,
v.From, v.From,
p.Items[0].ID) p.Items[0].ID)
case XMPPNS_AVATAR_PEP_METADATA: case XMPPNS_AVATAR_PEP_METADATA:
return handleAvatarMetadata(p.Items[0].Body, return handleAvatarMetadata(p.Items[0].Body,
v v.From)
}
}*/ }*/
} }
case v.Query.XMLName.Local == "": case v.Query.XMLName.Local == "":
@@ -1049,6 +1083,8 @@ type clientIQ struct {
Query XMLElement `xml:",any"` Query XMLElement `xml:",any"`
Error clientError Error clientError
Bind bindBind Bind bindBind
InnerXML []byte `xml:",innerxml"`
} }
type clientError struct { type clientError struct {

99
vendor/github.com/matterbridge/go-xmpp/xmpp_disco.go generated vendored Normal file
View File

@@ -0,0 +1,99 @@
package xmpp
import (
"encoding/xml"
)
const (
XMPPNS_DISCO_ITEMS = "http://jabber.org/protocol/disco#items"
XMPPNS_DISCO_INFO = "http://jabber.org/protocol/disco#info"
)
type clientDiscoFeature struct {
XMLName xml.Name `xml:"feature"`
Var string `xml:"var,attr"`
}
type clientDiscoIdentity struct {
XMLName xml.Name `xml:"identity"`
Category string `xml:"category,attr"`
Type string `xml:"type,attr"`
Name string `xml:"name,attr"`
}
type clientDiscoQuery struct {
XMLName xml.Name `xml:"query"`
Features []clientDiscoFeature `xml:"feature"`
Identities []clientDiscoIdentity `xml:"identity"`
}
type clientDiscoItem struct {
XMLName xml.Name `xml:"item"`
Jid string `xml:"jid,attr"`
Node string `xml:"node,attr"`
Name string `xml:"name,attr"`
}
type clientDiscoItemsQuery struct {
XMLName xml.Name `xml:"query"`
Items []clientDiscoItem `xml:"item"`
}
type DiscoIdentity struct {
Category string
Type string
Name string
}
type DiscoItem struct {
Jid string
Name string
Node string
}
type DiscoResult struct {
Features []string
Identities []DiscoIdentity
}
type DiscoItems struct {
Jid string
Items []DiscoItem
}
func clientFeaturesToReturn(features []clientDiscoFeature) []string {
var ret []string
for _, feature := range features {
ret = append(ret, feature.Var)
}
return ret
}
func clientIdentitiesToReturn(identities []clientDiscoIdentity) []DiscoIdentity {
var ret []DiscoIdentity
for _, id := range identities {
ret = append(ret, DiscoIdentity{
Category: id.Category,
Type: id.Type,
Name: id.Name,
})
}
return ret
}
func clientDiscoItemsToReturn(items []clientDiscoItem) []DiscoItem {
var ret []DiscoItem
for _, item := range items {
ret = append(ret, DiscoItem{
Jid: item.Jid,
Name: item.Name,
Node: item.Node,
})
}
return ret
}

View File

@@ -10,10 +10,26 @@ const IQTypeSet = "set"
const IQTypeResult = "result" const IQTypeResult = "result"
func (c *Client) Discovery() (string, error) { func (c *Client) Discovery() (string, error) {
const namespace = "http://jabber.org/protocol/disco#items"
// use getCookie for a pseudo random id. // use getCookie for a pseudo random id.
reqID := strconv.FormatUint(uint64(getCookie()), 10) reqID := strconv.FormatUint(uint64(getCookie()), 10)
return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, namespace, "") return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, XMPPNS_DISCO_ITEMS, "")
}
// Discover information about a node
func (c *Client) DiscoverNodeInfo(node string) (string, error) {
query := fmt.Sprintf("<query xmlns='%s' node='%s'/>", XMPPNS_DISCO_INFO, node)
return c.RawInformation(c.jid, c.domain, "info3", IQTypeGet, query)
}
// Discover items that the server exposes
func (c *Client) DiscoverServerItems() (string, error) {
return c.DiscoverEntityItems(c.domain)
}
// Discover items that an entity exposes
func (c *Client) DiscoverEntityItems(jid string) (string, error) {
query := fmt.Sprintf("<query xmlns='%s'/>", XMPPNS_DISCO_ITEMS)
return c.RawInformation(c.jid, jid, "info1", IQTypeGet, query)
} }
// RawInformationQuery sends an information query request to the server. // RawInformationQuery sends an information query request to the server.

6
vendor/modules.txt vendored
View File

@@ -15,7 +15,7 @@ github.com/Philipp15b/go-steam/protocol/steamlang
github.com/Philipp15b/go-steam/rwu github.com/Philipp15b/go-steam/rwu
github.com/Philipp15b/go-steam/socialcache github.com/Philipp15b/go-steam/socialcache
github.com/Philipp15b/go-steam/steamid github.com/Philipp15b/go-steam/steamid
# github.com/Rhymen/go-whatsapp v0.1.0 # github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334
github.com/Rhymen/go-whatsapp github.com/Rhymen/go-whatsapp
github.com/Rhymen/go-whatsapp/binary github.com/Rhymen/go-whatsapp/binary
github.com/Rhymen/go-whatsapp/binary/proto github.com/Rhymen/go-whatsapp/binary/proto
@@ -95,7 +95,7 @@ github.com/labstack/gommon/random
github.com/lrstanley/girc github.com/lrstanley/girc
# github.com/magiconair/properties v1.8.1 # github.com/magiconair/properties v1.8.1
github.com/magiconair/properties github.com/magiconair/properties
# github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d # github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048
github.com/matterbridge/Rocket.Chat.Go.SDK/models github.com/matterbridge/Rocket.Chat.Go.SDK/models
github.com/matterbridge/Rocket.Chat.Go.SDK/realtime github.com/matterbridge/Rocket.Chat.Go.SDK/realtime
github.com/matterbridge/Rocket.Chat.Go.SDK/rest github.com/matterbridge/Rocket.Chat.Go.SDK/rest
@@ -103,7 +103,7 @@ github.com/matterbridge/Rocket.Chat.Go.SDK/rest
github.com/matterbridge/discordgo github.com/matterbridge/discordgo
# github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible # github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible
github.com/matterbridge/emoji github.com/matterbridge/emoji
# github.com/matterbridge/go-xmpp v0.0.0-20200329150250-5812999b292b # github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050
github.com/matterbridge/go-xmpp github.com/matterbridge/go-xmpp
# github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6 # github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6
github.com/matterbridge/gomatrix github.com/matterbridge/gomatrix