commit 2f812744e07413743b8d33c0ea79ada031b092d4
Author: Krzysiek Madejski <krzysztof.madejski@epf.org.pl>
Date: Thu Feb 21 20:28:13 2019 +0000
diff --git a/README.md b/README.md
index 6694c62..82332ba 100644
--- a/README.md
+++ b/README.md
@@ -186 +187 @@
+ [WhatsApp][mb-whatsapp] |
@@ -796 +807 @@
* [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)
### 3rd party via matterbridge api
@@ -2766 +2787 @@ 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
* xmpp - https://github.com/mattn/go-xmpp
+* whatsapp - https://github.com/Rhymen/go-whatsapp/
* zulip - https://github.com/ifo/gozulipbot
<!-- Links -->
@@ -2894 +2925 @@ Matterbridge wouldn't exist without these libraries:
+ [mb-whatsapp]: https://www.whatsapp.com/
diff --git a/bridge/config/config.go b/bridge/config/config.go
index 98f3f2a..f473ab0 100644
--- a/bridge/config/config.go
+++ b/bridge/config/config.go
@@ -1856 +1857 @@ type BridgeValues struct {
Telegram map[string]Protocol
Rocketchat map[string]Protocol
SSHChat map[string]Protocol
+ WhatsApp map[string]Protocol // TODO is this struct used? Search for "SlackLegacy" for example didn't return any results
Zulip map[string]Protocol
General Protocol
Gateway []Gateway
diff --git a/bridge/whatsapp/handlers.go b/bridge/whatsapp/handlers.go
new file mode 100644
index 0000000..456f200
--- /dev/null
+++ b/bridge/whatsapp/handlers.go
@@ -00 +1104 @@
+package bwhatsapp
+
+import (
+ "strings"
+ "time"
+
+ "github.com/42wim/matterbridge/bridge/config"
+
+ "github.com/Rhymen/go-whatsapp"
+
+ whatsappExt "maunium.net/go/mautrix-whatsapp/whatsapp-ext"
+)
+
+/*
+Implement handling messages coming from WhatsApp
+Check:
+- https://github.com/Rhymen/go-whatsapp#add-message-handlers
+- https://github.com/Rhymen/go-whatsapp/blob/master/handler.go
+- https://github.com/tulir/mautrix-whatsapp/tree/master/whatsapp-ext for more advanced command handling
+*/
+
+// HandleError received from WhatsApp
+func (b *Bwhatsapp) HandleError(err error) {
+ b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types
+}
+
+// HandleTextMessage sent from WhatsApp, relay it to the brige
+func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
+ if message.Info.FromMe { // || !strings.Contains(strings.ToLower(message.Text), "@echo") {
+ return
+ }
+ // whatsapp sends last messages to show context , cut them
+ if message.Info.Timestamp < b.startedAt {
+ return
+ }
+
+ messageTime := time.Unix(int64(message.Info.Timestamp), 0) // TODO check how behaves between timezones
+ groupJid := message.Info.RemoteJid
+
+ senderJid := message.Info.SenderJid
+ if len(senderJid) == 0 {
+ // TODO workaround till https://github.com/Rhymen/go-whatsapp/issues/86 resolved
+ senderJid = *message.Info.Source.Participant
+ }
+
+ // translate sender's Jid to the nicest username we can get
+ senderName := b.getSenderName(senderJid)
+ if senderName == "" {
+ senderName = "Someone" // don't expose telephone number
+ }
+
+ extText := message.Info.Source.Message.ExtendedTextMessage
+ if extText != nil && extText.ContextInfo != nil && extText.ContextInfo.MentionedJid != nil {
+ // handle user mentions
+ for _, mentionedJid := range extText.ContextInfo.MentionedJid {
+ numberAndSuffix := strings.SplitN(mentionedJid, "@", 2)
+
+ // mentions comes as telephone numbers and we don't want to expose it to other bridges
+ // replace it with something more meaninful to others
+ mention := b.getSenderNotify(numberAndSuffix[0] + whatsappExt.NewUserSuffix)
+ if mention == "" {
+ mention = "someone"
+ }
+ message.Text = strings.Replace(message.Text, "@"+numberAndSuffix[0], "@"+mention, 1)
+ }
+ }
+
+ b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJid, b.Account)
+ rmsg := config.Message{
+ UserID: senderJid,
+ Username: senderName,
+ Text: message.Text,
+ Timestamp: messageTime,
+ Channel: groupJid,
+ Account: b.Account,
+ Protocol: b.Protocol,
+ Extra: make(map[string][]interface{}),
+ // ParentID: TODO, // TODO handle thread replies // map from Info.QuotedMessageID string
+ // Event string `json:"event"`
+ // Gateway string // will be added during message processing
+ ID: message.Info.Id}
+
+ if avatarURL, exists := b.userAvatars[senderJid]; exists {
+ rmsg.Avatar = avatarURL
+ }
+
+ b.Log.Debugf("<= Message is %#v", rmsg)
+ b.Remote <- rmsg
+}
+
+//
+//func (b *Bwhatsapp) HandleImageMessage(message whatsapp.ImageMessage) {
+// fmt.Println(message) // TODO implement
+//}
+//
+//func (b *Bwhatsapp) HandleVideoMessage(message whatsapp.VideoMessage) {
+// fmt.Println(message) // TODO implement
+//}
+//
+//func (b *Bwhatsapp) HandleJsonMessage(message string) {
+// fmt.Println(message) // TODO implement
+//}
+// TODO HandleRawMessage
+// TODO HandleAudioMessage
diff --git a/bridge/whatsapp/helpers.go b/bridge/whatsapp/helpers.go
new file mode 100644
index 0000000..5268ba3
--- /dev/null
+++ b/bridge/whatsapp/helpers.go
@@ -00 +184 @@
+package bwhatsapp
+
+import (
+ "encoding/gob"
+ "errors"
+ "os"
+
+ qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go"
+ "github.com/Rhymen/go-whatsapp"
+)
+
+func qrFromTerminal(invert bool) chan string {
+ qr := make(chan string)
+ go func() {
+ terminal := qrcodeTerminal.New()
+ if invert {
+ terminal = qrcodeTerminal.New2(qrcodeTerminal.ConsoleColors.BrightWhite, qrcodeTerminal.ConsoleColors.BrightBlack, qrcodeTerminal.QRCodeRecoveryLevels.Medium)
+ }
+
+ terminal.Get(<-qr).Print()
+ }()
+
+ return qr
+}
+
+func (b *Bwhatsapp) readSession() (whatsapp.Session, error) {
+ session := whatsapp.Session{}
+ sessionFile := b.Config.GetString(sessionFile)
+
+ if sessionFile == "" {
+ return session, errors.New("if you won't set SessionFile then you will need to scan QR code on every restart")
+ }
+
+ file, err := os.Open(sessionFile)
+ if err != nil {
+ return session, err
+ }
+ defer file.Close()
+ decoder := gob.NewDecoder(file)
+ err = decoder.Decode(&session)
+ if err != nil {
+ return session, err
+ }
+ return session, nil
+}
+
+func (b *Bwhatsapp) writeSession(session whatsapp.Session) error {
+ sessionFile := b.Config.GetString(sessionFile)
+
+ if sessionFile == "" {
+ // we already sent a warning while starting the bridge, so let's be quiet here
+ return nil
+ }
+
+ file, err := os.Create(sessionFile)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+ encoder := gob.NewEncoder(file)
+ err = encoder.Encode(session)
+
+ return err
+}
+
+func (b *Bwhatsapp) getSenderName(senderJid string) string {
+ if sender, exists := b.users[senderJid]; exists {
+ if sender.Name != "" {
+ return sender.Name
+ }
+ // if user is not in phone contacts
+ // it is the most obvious scenario unless you sync your phone contacts with some remote updated source
+ // users can change it in their WhatsApp settings -> profile -> click on Avatar
+ return sender.Notify
+ }
+ return ""
+}
+
+func (b *Bwhatsapp) getSenderNotify(senderJid string) string {
+ if sender, exists := b.users[senderJid]; exists {
+ return sender.Notify
+ }
+ return ""
+}
diff --git a/bridge/whatsapp/whatsapp.go b/bridge/whatsapp/whatsapp.go
new file mode 100644
index 0000000..e17339d
--- /dev/null
+++ b/bridge/whatsapp/whatsapp.go
@@ -00 +1305 @@
+package bwhatsapp
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/42wim/matterbridge/bridge"
+ "github.com/42wim/matterbridge/bridge/config"
+
+ "github.com/Rhymen/go-whatsapp"
+
+ whatsappExt "maunium.net/go/mautrix-whatsapp/whatsapp-ext"
+)
+
+const (
+ // Account config parameters
+ cfgNumber = "Number"
+ qrOnWhiteTerminal = "QrOnWhiteTerminal"
+ sessionFile = "SessionFile"
+)
+
+// Bwhatsapp Bridge structure keeping all the information needed for relying
+type Bwhatsapp struct {
+ *bridge.Config
+
+ // https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L18-L21
+ session *whatsapp.Session
+ conn *whatsapp.Conn
+ // https://github.com/tulir/mautrix-whatsapp/blob/master/whatsapp-ext/whatsapp.go
+ connExt *whatsappExt.ExtendedConn
+ startedAt uint64
+
+ users map[string]whatsapp.Contact
+ userAvatars map[string]string
+}
+
+// New Create a new WhatsApp bridge. This will be called for each [whatsapp.<server>] entry you have in the config file
+func New(cfg *bridge.Config) bridge.Bridger {
+ number := cfg.GetString(cfgNumber)
+ if number == "" {
+ cfg.Log.Fatalf("Missing configuration for WhatsApp bridge: Number")
+ }
+
+ b := &Bwhatsapp{
+ Config: cfg,
+
+ users: make(map[string]whatsapp.Contact),
+ userAvatars: make(map[string]string),
+ }
+ return b
+}
+
+// Connect to WhatsApp. Required implementation of the Bridger interface
+// https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16
+func (b *Bwhatsapp) Connect() error {
+ b.RLock() // TODO do we need locking for Whatsapp?
+ defer b.RUnlock()
+
+ number := b.GetString(cfgNumber)
+ if number == "" {
+ return errors.New("WhatsApp's telephone Number need to be configured")
+ }
+
+ // https://github.com/Rhymen/go-whatsapp#creating-a-connection
+ b.Log.Debugln("Connecting to WhatsApp..")
+ conn, err := whatsapp.NewConn(20 * time.Second)
+ if err != nil {
+ return errors.New("failed to connect to WhatsApp: " + err.Error())
+ }
+
+ b.conn = conn
+ b.connExt = whatsappExt.ExtendConn(b.conn)
+ // TODO do we want to use it? b.connExt.SetClientName("Matterbridge WhatsApp bridge", "mb-wa")
+
+ b.conn.AddHandler(b)
+ b.Log.Debugln("WhatsApp connection successful")
+
+ // load existing session in order to keep it between restarts
+ if b.session == nil {
+ var session whatsapp.Session
+ session, err = b.readSession()
+
+ if err == nil {
+ b.Log.Debugln("Restoring WhatsApp session..")
+
+ // https://github.com/Rhymen/go-whatsapp#restore
+ session, err = b.conn.RestoreSession(session)
+ if err != nil {
+ // TODO return or continue to normal login?
+ // restore session connection timed out (I couldn't get over it without logging in again)
+ return errors.New("failed to restore session: " + err.Error())
+ }
+
+ b.session = &session
+ b.Log.Debugln("Session restored successfully!")
+ } else {
+ b.Log.Warn(err.Error())
+ }
+ }
+
+ // login to a new session
+ if b.session == nil {
+ err = b.Login()
+ if err != nil {
+ return err
+ }
+ }
+ b.startedAt = uint64(time.Now().Unix())
+
+ _, err = b.conn.Contacts()
+ if err != nil {
+ return fmt.Errorf("error on update of contacts: %v", err)
+ }
+
+ // map all the users
+ for id, contact := range b.conn.Store.Contacts {
+ if !isGroupJid(id) && id != "status@broadcast" {
+ // it is user
+ b.users[id] = contact
+ }
+ }
+
+ // get user avatar asynchronously
+ go func() {
+ b.Log.Debug("Getting user avatars..")
+
+ for jid := range b.users {
+ info, err := b.connExt.GetProfilePicThumb(jid)
+ if err != nil {
+ b.Log.Warnf("Could not get profile photo of %s: %v", jid, err)
+
+ } else {
+ // TODO any race conditions here?
+ b.userAvatars[jid] = info.URL
+ }
+ }
+ b.Log.Debug("Finished getting avatars..")
+ }()
+
+ return nil
+}
+
+// Login to WhatsApp creating a new session. This will require to scan a QR code on your mobile device
+func (b *Bwhatsapp) Login() error {
+ b.Log.Debugln("Logging in..")
+
+ invert := b.GetBool(qrOnWhiteTerminal) // false is the default
+ qrChan := qrFromTerminal(invert)
+
+ session, err := b.conn.Login(qrChan)
+ if err != nil {
+ b.Log.Warnln("Failed to log in:", err)
+ return err
+ }
+ b.session = &session
+
+ b.Log.Infof("Logged into session: %#v", session)
+ b.Log.Infof("Connection: %#v", b.conn)
+
+ err = b.writeSession(session)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error saving session: %v\n", err)
+ }
+
+ // TODO change connection strings to configured ones longClientName:"github.com/rhymen/go-whatsapp", shortClientName:"go-whatsapp"}" prefix=whatsapp
+ // TODO get also a nice logo
+
+ // TODO notification about unplugged and dead battery
+ // conn.Info: Wid, Pushname, Connected, Battery, Plugged
+
+ return nil
+}
+
+// Disconnect is called while reconnecting to the bridge
+// TODO 42wim Documentation would be helpful on when reconnects happen and what should be done in this function
+// Required implementation of the Bridger interface
+// https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16
+func (b *Bwhatsapp) Disconnect() error {
+ // We could Logout, but that would close the session completely and would require a new QR code scan
+ // https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L377-L381
+ return nil
+}
+
+func isGroupJid(identifier string) bool {
+ return strings.HasSuffix(identifier, "@g.us") || strings.HasSuffix(identifier, "@temp")
+}
+
+// JoinChannel Join a WhatsApp group specified in gateway config as channel='number-id@g.us' or channel='Channel name'
+// Required implementation of the Bridger interface
+// https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16
+func (b *Bwhatsapp) JoinChannel(channel config.ChannelInfo) error {
+ byJid := isGroupJid(channel.Name)
+
+ // verify if we are member of the given group
+ if byJid {
+ // channel.Name specifies static group jID, not the name
+ if _, exists := b.conn.Store.Contacts[channel.Name]; !exists {
+ return fmt.Errorf("account doesn't belong to group with jid %s", channel.Name)
+ }
+ } else {
+ // channel.Name specifies group name that might change, warn about it
+ var jids []string
+ for id, contact := range b.conn.Store.Contacts {
+ if isGroupJid(id) && contact.Name == channel.Name {
+ jids = append(jids, id)
+ }
+ }
+
+ switch len(jids) {
+ case 0:
+ // didn't match any group - print out possibilites
+ // TODO sort
+ // copy b;
+ //sort.Slice(people, func(i, j int) bool {
+ // return people[i].Age > people[j].Age
+ //})
+ for id, contact := range b.conn.Store.Contacts {
+ if isGroupJid(id) {
+ b.Log.Infof("%s %s", contact.Jid, contact.Name)
+ }
+ }
+ return fmt.Errorf("please specify group's JID from the list above instead of the name '%s'", channel.Name)
+
+ case 1:
+ return fmt.Errorf("group name might change. Please configure gateway with channel=\"%v\" instead of channel=\"%v\"", jids[0], channel.Name)
+
+ default:
+ return fmt.Errorf("there is more than one group with name '%s'. Please specify one of JIDs as channel name: %v", channel.Name, jids)
+ }
+ }
+
+ return nil
+}
+
+// Send a message from the bridge to WhatsApp
+// Required implementation of the Bridger interface
+// https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16
+func (b *Bwhatsapp) Send(msg config.Message) (string, error) {
+ b.Log.Debugf("=> Receiving %#v", msg)
+
+ // Delete message
+ if msg.Event == config.EventMsgDelete {
+ if msg.ID == "" {
+ // No message ID in case action is executed on a message sent before the bridge was started
+ // and then the bridge cache doesn't have this message ID mapped
+
+ // TODO 42wim Doesn't the app get clogged with a ton of IDs after some time of running?
+ // WhatsApp allows to set any ID so in that case we could use external IDs and don't do mapping
+ // but external IDs are not set
+ return "", nil
+ }
+ // TODO delete message on WhatsApp https://github.com/Rhymen/go-whatsapp/issues/100
+ return "", nil
+ }
+
+ // Edit message
+ if msg.ID != "" {
+ b.Log.Debugf("updating message with id %s", msg.ID)
+
+ msg.Text += " (edited)"
+ // TODO handle edit as a message reply with updated text
+ }
+
+ //// TODO Handle Upload a file
+ //if msg.Extra != nil {
+ // for _, rmsg := range helper.HandleExtra(&msg, b.General) {
+ // b.c.SendMessage(roomID, rmsg.Username+rmsg.Text)
+ // }
+ // if len(msg.Extra["file"]) > 0 {
+ // return b.handleUploadFile(&msg, roomID)
+ // }
+ //}
+
+ // Post text message
+ text := whatsapp.TextMessage{
+ Info: whatsapp.MessageInfo{
+ RemoteJid: msg.Channel, // which equals to group id
+ },
+ Text: msg.Username + msg.Text,
+ }
+
+ b.Log.Debugf("=> Sending %#v", msg)
+
+ // create message ID
+ // TODO follow and act if https://github.com/Rhymen/go-whatsapp/issues/101 implemented
+ bytes := make([]byte, 10)
+ if _, err := rand.Read(bytes); err != nil {
+ b.Log.Warn(err.Error())
+ }
+ text.Info.Id = strings.ToUpper(hex.EncodeToString(bytes))
+
+ err := b.conn.Send(text)
+
+ return text.Info.Id, err
+}
+
+// TODO do we want that? to allow login with QR code from a bridged channel? https://github.com/tulir/mautrix-whatsapp/blob/513eb18e2d59bada0dd515ee1abaaf38a3bfe3d5/commands.go#L76
+//func (b *Bwhatsapp) Command(cmd string) string {
+// return ""
+//}
diff --git a/gateway/bridgemap/bridgemap.go b/gateway/bridgemap/bridgemap.go
index 20577dc..1ad013e 100644
--- a/gateway/bridgemap/bridgemap.go
+++ b/gateway/bridgemap/bridgemap.go
@@ -136 +137 @@ import (
"github.com/42wim/matterbridge/bridge/sshchat"
"github.com/42wim/matterbridge/bridge/steam"
"github.com/42wim/matterbridge/bridge/telegram"
+ "github.com/42wim/matterbridge/bridge/whatsapp"
"github.com/42wim/matterbridge/bridge/xmpp"
"github.com/42wim/matterbridge/bridge/zulip"
)
@@ -306 +317 @@ var FullMap = map[string]bridge.Factory{
"sshchat": bsshchat.New,
"steam": bsteam.New,
"telegram": btelegram.New,
+ "whatsapp": bwhatsapp.New,
"xmpp": bxmpp.New,
"zulip": bzulip.New,
}
diff --git a/go.mod b/go.mod
index 15fbb83..0d79c67 100644
--- a/go.mod
+++ b/go.mod
@@ -214 +215 @@ module github.com/42wim/matterbridge
require (
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
+ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 // indirect
github.com/Jeffail/gabs v1.1.1 // indirect
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
+ github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884
github.com/bwmarrin/discordgo v0.19.0
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
github.com/fsnotify/fsnotify v1.4.7
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
- github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc // indirect
github.com/google/gops v0.3.5
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f // indirect
@@ -477 +486 @@ require (
github.com/russross/blackfriday v2.0.0+incompatible
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296
- github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect
github.com/sirupsen/logrus v1.3.0
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // indirect
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
@@ -6510 +658 @@ require (
go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.9.1 // indirect
- golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 // indirect
- golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect
- gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
+ maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3
)
diff --git a/go.sum b/go.sum
index 4df46c7..f0fabad 100644
--- a/go.sum
+++ b/go.sum
@@ -111 +117 @@
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 h1:IZtuWGfzQnKnCSu+vl8WGLhpVQ5Uvy3rlSwqXSg+sQg=
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557/go.mod h1:jL0YSXMs/txjtGJ4PWrmETOk6KUHMDPMshgQZlTeB3Y=
+github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII=
+github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 h1:v/zr4ns/4sSahF9KBm4Uc933bLsEEv7LuT63CJ019yo=
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 h1:xZBoq249G9MSt+XuY7sVQzcfONJ6IQuwpCK+KAaOpnY=
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
+github.com/Rhymen/go-whatsapp v0.0.0-20181218094654-2ca6af00572c h1:ldRXgMEfKmzBomrZusl3edG9AGEeztA7jovLEQy62us=
+github.com/Rhymen/go-whatsapp v0.0.0-20181218094654-2ca6af00572c/go.mod h1:MSDmePOOkbFFbVW2WRRppBcbA+aabwpXRgyIIG7jDFQ=
+github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884 h1:2AxfzkQi2L4QGBvUCZoWD6hQuUJa5MG54wiYyNqJlf4=
+github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc=
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@@ -2118 +2722 @@ github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc h1:wdhDSKrkYy24mcfzuA3oYm58h0QkyXjwERCkzJDP5kA=
github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/gops v0.3.5 h1:SIWvPLiYvy5vMwjxB3rVFTE4QBhUFj2KKWr3Xm7CKhw=
github.com/google/gops v0.3.5/go.mod h1:pMQgrscwEK/aUSW1IFSaBPbJX82FPHWaSoJw1axQfD0=
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo=
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA=
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ=
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/schema v1.0.2 h1:sAgNfOcNYvdDSrzGHVy9nzCQahG+qmsg+nE8dK85QRA=
github.com/gorilla/schema v1.0.2/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
@@ -866 +967 @@ github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRU
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
@@ -1206 +1317 @@ github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
@@ -1288 +14013 @@ github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 h1:8RLq547MSVc6vhO
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/skip2/go-qrcode v0.0.0-20171229120447-cf5f9fa2f0d8/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
+github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
+github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 h1:lXQ+j+KwZcbwrbgU0Rp4Eglg3EJLHbuZU3BbOqAGBmg=
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
@@ -18514 +20223 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc=
+golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 h1:BkNcmLtAVeWe9h5k0jt24CQgaG5vb4x/doFbAiEC/Ho=
golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM=
+golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181212120007-b05ddf57801d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
@@ -2083 +2349 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
+maunium.net/go/maulogger/v2 v2.0.0/go.mod h1:Hbbkq3NV6jvJodByZu1mgEF3fpT7Kz9z0MjEZ3/BusI=
+maunium.net/go/mautrix v0.1.0-alpha.3/go.mod h1:GTVu6WDHR+98DKOrYetWsXorvUeKQV3jsSWO6ScbuFI=
+maunium.net/go/mautrix-appservice v0.1.0-alpha.3/go.mod h1:wOnWOIuprYad7ly12rHIo3JLCPh4jwvx1prVrAB9RhM=
+maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3 h1:A18t5Lp7I3aK0V7B7zdpb0hb/PBlu0X/Ai2AyU/XEk4=
+maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3/go.mod h1:r5E3J4urDEsjfui9OYZYMLBfCliaAqcCwM2xeczta6k=
diff --git a/matterbridge.toml.sample b/matterbridge.toml.sample
index 33bf99c..55f3ab7 100644
--- a/matterbridge.toml.sample
+++ b/matterbridge.toml.sample
@@ -117210 +117245 @@ StripNick=false
#OPTIONAL (default false)
ShowTopicChange=false
+
+
+###################################################################
+#
+# WhatsApp
+#
+###################################################################
+
+[whatsapp.bridge]
+
+# Number you will use as a relay bot. Tip: Get some disposable sim card, don't rely on your own number.
+Number="+48111222333"
+
+# First time that you login you will need to scan QR code, then credentials willl be saved in a session file
+# If you won't set SessionFile then you will need to scan QR code on every restart
+# optional (by default the session is stored only in memory, till restarting matterbridge)
+SessionFile="session-48111222333.gob"
+
+# If your terminal is white we need to invert QR code in order for it to be scanned properly
+# optional (default false)
+QrOnWhiteTerminal=true
+
+# Messages will be seen by other WhatsApp contacts as coming from the bridge. Original nick will be part of the message.
+RemoteNickFormat="@{NICK}: "
+
+# extra label that can be used in the RemoteNickFormat
+# optional (default empty)
+Label="Organization"
+
+
+
###################################################################
-#zulip section
+#
+# zulip
+#
###################################################################
+
[zulip]
+
#You can configure multiple servers "[zulip.name]" or "[zulip.name2]"
#In this example we use [zulip.streamchat]
#REQUIRED
@@ -137336 +140841 @@ name="gateway1"
##OPTIONAL (default false)
enable=true
- #[[gateway.in]] specifies the account and channels we will receive messages from.
- #The following example bridges between mattermost and irc
+ # [[gateway.in]] specifies the account and channels we will receive messages from.
+ # The following example bridges between mattermost and irc
- #account specified above
- #REQUIRED
+ # account specified above
+ # REQUIRED
- #channel to connect on that account
- #How to specify them for the different bridges:
+
+ # channel to connect on that account
+ # How to specify them for the different bridges:
- #irc - #channel (# is required) (this needs to be lowercase!)
- #mattermost - channel (the channel name as seen in the URL, not the displayname)
- #gitter - username/room
- #xmpp - channel
- #slack - channel (without the #)
- # - ID:C123456 (where C123456 is the channel ID) does not work with webhook
- #discord - channel (without the #)
- # - ID:123456789 (where 123456789 is the channel ID)
+ # irc - #channel (# is required) (this needs to be lowercase!)
+ # mattermost - channel (the channel name as seen in the URL, not the displayname)
+ # gitter - username/room
+ # xmpp - channel
+ # slack - channel (without the #)
+ # - ID:C123456 (where C123456 is the channel ID) does not work with webhook
+ # discord - channel (without the #)
+ # - ID:123456789 (where 123456789 is the channel ID)
- #telegram - chatid (a large negative number, eg -123456789)
+ # telegram - chatid (a large negative number, eg -123456789)
- #hipchat - id_channel (see https://www.hipchat.com/account/xmpp for the correct channel)
- #rocketchat - #channel (# is required (also needed for private channels!)
- #matrix - #channel:server (eg #yourchannel:matrix.org)
- # - encrypted rooms are not supported in matrix
- #steam - chatid (a large number).
+ # hipchat - id_channel (see https://www.hipchat.com/account/xmpp for the correct channel)
+ # rocketchat - #channel (# is required (also needed for private channels!)
+ # matrix - #channel:server (eg #yourchannel:matrix.org)
+ # - encrypted rooms are not supported in matrix
+ # steam - chatid (a large number).
- #zulip - stream (without the #)
+ # whatsapp - 48111222333-123455678999@g.us A unique group JID;
+ # if you specify an empty string bridge will list all the possibilities
+ # - "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
+ # zulip - stream (without the #)
- #REQUIRED
+ # REQUIRED