4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-06-26 17:49:23 +00:00

Compare commits

...

34 Commits

Author SHA1 Message Date
Wim
c4c6aff9a5 Release v1.11.1 2018-08-06 17:49:14 +02:00
Wim
d71850cef6 Use UserID to look for avatar instead of username (slack). Closes #472 2018-08-06 16:44:15 +02:00
Wim
2597c9bfac Clip too long messages sent to discord (discord). Closes #440 2018-07-22 00:28:17 +02:00
Wim
93307b57aa Skip empty messages being sent with the webhook (discord). #469 2018-07-21 23:19:11 +02:00
Wim
618953c865 Remove ununsed function (slack) 2018-07-13 23:28:23 +02:00
Wim
e04dd78624 Add support for slack channels by ID. Closes #436 2018-07-13 23:23:11 +02:00
Wim
fa0c4025f7 Fix avatar uploads to work with MediaDownloadPath. Closes #454 2018-07-11 23:44:29 +02:00
2d2d185200 Stop numbers being stripped after non-color control codes (irc) (#465)
Currently numbers are stripped not just after the color control code (\x03) but also after other formatting such as bold (\x02) and italic (\x1D), which is both unnecessary and leads to missing text from irc. This fixes that by only stripping numbers after the color control code.
2018-07-11 22:50:49 +02:00
Wim
cb7278eb50 Use nickname instead of username if defined (mattermost). Closes #452 2018-07-03 22:41:09 +02:00
Wim
89aa114192 Add GetNickname and UpdateUser functions
When we get an user_updated event from mattermost we also actually update
the user, so the nicknames/usernames are also updated
2018-07-03 22:35:44 +02:00
Wim
ed062e0ce5 Add a space before url in file uploads (discord). Closes #461 2018-06-29 22:35:29 +02:00
Wim
a69ef8402b Fix previous commit 2018-06-28 21:19:02 +02:00
Wim
8779f67d2d Allow join-leave and topic changes to webhook (discord) 2018-06-28 21:14:31 +02:00
Wim
e4b72136b8 Fix possible panic. #448 2018-06-19 22:53:45 +02:00
Wim
4ff5091bc2 Bump version 2018-06-19 00:41:49 +02:00
Wim
6f131250f1 Release v1.11.0 2018-06-19 00:28:16 +02:00
Wim
221a63d980 Fix build (telegram) 2018-06-18 23:49:28 +02:00
Wim
d02eda147c Add support for MessageFormat=htmlnick (telegram). #444 2018-06-18 23:38:52 +02:00
9b25716136 Fix typo and add minor clarification. (#450) 2018-06-18 23:00:12 +02:00
6628a47f23 Add channel password support for XMPP (#451) 2018-06-18 22:55:45 +02:00
Wim
ec0e6bc3f8 Add support for mattermost 5.x 2018-06-17 23:48:06 +02:00
Wim
d2c02be3a0 Handle slack attachments sent to mattermost. Closes #447 2018-06-16 00:11:15 +02:00
594492fbdd Add Title from attachment slack message (#446) 2018-06-13 21:58:51 +02:00
Wim
bd9ea7a88d Add MediaDownloadBlacklist option. Closes #442 2018-06-09 14:35:02 +02:00
51327a4056 Reconnect on quit. (irc) See #431 (#445)
* potential fixes for #431
* go: fix formatting/gofmt/goreturns
2018-06-09 12:47:40 +02:00
33bd60528b Add config option MediaDownloadPath (#443)
* Add config option MediaUploadPath

MediaDownloadPath can be used instead of MediaServerUpload, for when your
webserver is on the same system as matterbridge and matterbridge has
write access to the serve dir.

* Limit length of hash in MediaServer urls to 8chars

Full SHA256 is unnecessary for uniqueness.
Also; if a file has the same first 8 charachters of the SHA256 hash,
it's still not a problem, as long as the filename is not the same.
2018-06-08 22:30:35 +02:00
Wim
7e54474111 Add info about markdown (telegram) 2018-06-06 01:00:00 +02:00
Wim
e307069d62 Ignore messages from ourself. (sshchat) Closes #439 2018-06-06 00:51:42 +02:00
91db63294c Add message correction support for XMPP (#437)
It works worse than it could be, since message correction in XMPP
works differently compared to other messengers. XMPP replaces old
message with old ID with new message with new ID. Matterbridge
remembers only old ID, that's why you can edit a message from
XMPP to the gateway only once.

Edited messages from other networks to XMPP are handled correctly
though.
2018-05-29 23:29:51 +02:00
Wim
fd04e08c9c Update vendor matterbridge/go-xmpp 2018-05-29 23:28:19 +02:00
6576409d60 Prevent white or black color codes (irc) (#434) 2018-05-29 22:52:01 +02:00
045cb2058c Fix regexp in replaceMention (slack). (#435) 2018-05-29 22:49:10 +02:00
Wim
d03afc12fd Update changelog 2018-05-27 23:02:32 +02:00
Wim
48799a3cff Bump version 2018-05-27 22:46:30 +02:00
25 changed files with 423 additions and 146 deletions

View File

@ -49,7 +49,7 @@ Used by at least 2 projects. Feel free to make a PR to add your project to this
# Requirements
Accounts to one of the supported bridges
* [Mattermost](https://github.com/mattermost/platform/) 3.8.x - 3.10.x, 4.x
* [Mattermost](https://github.com/mattermost/platform/) 3.8.x - 3.10.x, 4.x, 5.x
* [IRC](http://www.mirc.com/servers.html)
* [XMPP](https://jabber.org)
* [Gitter](https://gitter.im)
@ -69,11 +69,13 @@ See https://github.com/42wim/matterbridge/wiki
# Installing
## Binaries
* Latest stable release [v1.10.1](https://github.com/42wim/matterbridge/releases/latest)
* Latest stable release [v1.11.1](https://github.com/42wim/matterbridge/releases/latest)
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
## Building
Go 1.8+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH)
Go 1.8+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH](https://golang.org/doc/code.html#GOPATH).
After Go is setup, download matterbridge to your $GOPATH directory.
```
cd $GOPATH

View File

@ -2,14 +2,15 @@ package api
import (
"encoding/json"
"net/http"
"sync"
"time"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"github.com/zfjagann/golang-ring"
"net/http"
"sync"
"time"
)
type Api struct {

View File

@ -2,14 +2,15 @@ package config
import (
"bytes"
"github.com/fsnotify/fsnotify"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
"os"
"strings"
"sync"
"time"
"github.com/fsnotify/fsnotify"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
)
const (
@ -73,6 +74,8 @@ type Protocol struct {
Jid string // xmpp
Label string // all protocols
Login string // mattermost, matrix
MediaDownloadBlackList []string
MediaDownloadPath string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server.
MediaDownloadSize int // all protocols
MediaServerDownload string
MediaServerUpload string
@ -123,7 +126,7 @@ type Protocol struct {
}
type ChannelOptions struct {
Key string // irc
Key string // irc, xmpp
WebhookURL string // discord
}

View File

@ -3,15 +3,18 @@ package bdiscord
import (
"bytes"
"fmt"
"regexp"
"strings"
"sync"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/bwmarrin/discordgo"
"regexp"
"strings"
"sync"
)
const MessageLength = 1950
type Bdiscord struct {
c *discordgo.Session
Channels []*discordgo.Channel
@ -126,16 +129,22 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
// Use webhook to send the message
if wID != "" {
// skip events
if msg.Event != "" {
if msg.Event != "" && msg.Event != config.EVENT_JOIN_LEAVE && msg.Event != config.EVENT_TOPIC_CHANGE {
return "", nil
}
b.Log.Debugf("Broadcasting using Webhook")
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
if fi.URL != "" {
msg.Text += fi.URL + " "
msg.Text += " " + fi.URL
}
}
// skip empty messages
if msg.Text == "" {
return "", nil
}
msg.Text = helper.ClipMessage(msg.Text, MessageLength)
err := b.c.WebhookExecute(
wID,
wToken,
@ -162,6 +171,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
// Upload a file if it exists
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
rmsg.Text = helper.ClipMessage(rmsg.Text, MessageLength)
b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text)
}
// check if we have files to upload (from slack, telegram or mattermost)
@ -170,6 +180,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
}
}
msg.Text = helper.ClipMessage(msg.Text, MessageLength)
// Edit message
if msg.ID != "" {
_, err := b.c.ChannelMessageEdit(channelID, msg.ID, msg.Username+msg.Text)

View File

@ -2,11 +2,12 @@ package bgitter
import (
"fmt"
"strings"
"github.com/42wim/go-gitter"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"strings"
)
type Bgitter struct {

View File

@ -3,12 +3,15 @@ package helper
import (
"bytes"
"fmt"
"github.com/42wim/matterbridge/bridge/config"
log "github.com/sirupsen/logrus"
"io"
"net/http"
"regexp"
"strings"
"time"
"unicode/utf8"
"github.com/42wim/matterbridge/bridge/config"
log "github.com/sirupsen/logrus"
)
func DownloadFile(url string) (*[]byte, error) {
@ -57,7 +60,7 @@ func HandleExtra(msg *config.Message, general *config.Protocol) []config.Message
for _, f := range extra[config.EVENT_FILE_FAILURE_SIZE] {
fi := f.(config.FileInfo)
text := fmt.Sprintf("file %s too big to download (%#v > allowed size: %#v)", fi.Name, fi.Size, general.MediaDownloadSize)
rmsg = append(rmsg, config.Message{Text: text, Username: "<system> ", Channel: msg.Channel})
rmsg = append(rmsg, config.Message{Text: text, Username: "<system> ", Channel: msg.Channel, Account: msg.Account})
}
return rmsg
}
@ -72,6 +75,19 @@ func GetAvatar(av map[string]string, userid string, general *config.Protocol) st
}
func HandleDownloadSize(flog *log.Entry, msg *config.Message, name string, size int64, general *config.Protocol) error {
// check blacklist here
for _, entry := range general.MediaDownloadBlackList {
if entry != "" {
re, err := regexp.Compile(entry)
if err != nil {
flog.Errorf("incorrect regexp %s for %s", entry, msg.Account)
continue
}
if re.MatchString(name) {
return fmt.Errorf("Matching blacklist %s. Not downloading %s", entry, name)
}
}
}
flog.Debugf("Trying to download %#v with size %#v", name, size)
if int(size) > general.MediaDownloadSize {
msg.Event = config.EVENT_FILE_FAILURE_SIZE
@ -100,3 +116,15 @@ func RemoveEmptyNewLines(msg string) string {
lines = strings.TrimRight(lines, "\n")
return lines
}
func ClipMessage(text string, length int) string {
// clip too long messages
if len(text) > length {
text = text[:length-len(" *message clipped*")]
if r, size := utf8.DecodeLastRuneInString(text); r == utf8.RuneError {
text = text[:len(text)-size]
}
text += " *message clipped*"
}
return text
}

View File

@ -4,14 +4,6 @@ import (
"bytes"
"crypto/tls"
"fmt"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/dfordsoft/golib/ic"
"github.com/lrstanley/girc"
"github.com/paulrosania/go-charset/charset"
_ "github.com/paulrosania/go-charset/data"
"github.com/saintfish/chardet"
"hash/crc32"
"io"
"io/ioutil"
@ -22,6 +14,15 @@ import (
"strings"
"time"
"unicode/utf8"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/dfordsoft/golib/ic"
"github.com/lrstanley/girc"
"github.com/paulrosania/go-charset/charset"
_ "github.com/paulrosania/go-charset/data"
"github.com/saintfish/chardet"
)
type Birc struct {
@ -114,18 +115,19 @@ func (b *Birc) Connect() error {
go func() {
for {
if err := i.Connect(); err != nil {
b.Log.Errorf("error: %s", err)
b.Log.Info("reconnecting in 30 seconds...")
time.Sleep(30 * time.Second)
i.Handlers.Clear(girc.RPL_WELCOME)
i.Handlers.Add(girc.RPL_WELCOME, func(client *girc.Client, event girc.Event) {
b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
// set our correct nick on reconnect if necessary
b.Nick = event.Source.Name
})
b.Log.Errorf("disconnect: error: %s", err)
} else {
return
b.Log.Info("disconnect: client requested quit")
}
b.Log.Info("reconnecting in 30 seconds...")
time.Sleep(30 * time.Second)
i.Handlers.Clear(girc.RPL_WELCOME)
i.Handlers.Add(girc.RPL_WELCOME, func(client *girc.Client, event girc.Event) {
b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
// set our correct nick on reconnect if necessary
b.Nick = event.Source.Name
})
}
}()
b.i = i
@ -250,7 +252,8 @@ func (b *Birc) doSend() {
username := msg.Username
if b.GetBool("Colornicks") {
checksum := crc32.ChecksumIEEE([]byte(msg.Username))
username = fmt.Sprintf("\x03%02d%s\x0F", checksum%0x10, msg.Username)
colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes
username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username)
}
if msg.Event == config.EVENT_USER_ACTION {
b.i.Cmd.Action(msg.Channel, username+msg.Text)
@ -394,7 +397,7 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
rmsg.Text += event.StripAction()
// strip IRC colors
re := regexp.MustCompile(`[[:cntrl:]](?:\d{1,2}(?:,\d{1,2})?)?`)
re := regexp.MustCompile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`)
rmsg.Text = re.ReplaceAllString(rmsg.Text, "")
// start detecting the charset

View File

@ -3,14 +3,15 @@ package bmatrix
import (
"bytes"
"fmt"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
matrix "github.com/matterbridge/gomatrix"
"mime"
"regexp"
"strings"
"sync"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
matrix "github.com/matterbridge/gomatrix"
)
type Bmatrix struct {

View File

@ -3,13 +3,14 @@ package bmattermost
import (
"errors"
"fmt"
"strings"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/42wim/matterbridge/matterclient"
"github.com/42wim/matterbridge/matterhook"
"github.com/rs/xid"
"strings"
)
type Bmattermost struct {
@ -184,6 +185,9 @@ func (b *Bmattermost) handleMatter() {
for message := range messages {
message.Avatar = helper.GetAvatar(b.avatarMap, message.UserID, b.General)
message.Account = b.Account
if nick := b.mc.GetNickName(message.UserID); nick != "" {
message.Username = nick
}
message.Text, ok = b.replaceAction(message.Text)
if ok {
message.Event = config.EVENT_USER_ACTION
@ -204,7 +208,7 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
}
// only download avatars if we have a place to upload them (configured mediaserver)
if b.General.MediaServerUpload != "" {
if b.General.MediaServerUpload != "" || b.General.MediaDownloadPath != "" {
b.handleDownloadAvatar(message.UserID, message.Channel)
}
@ -220,6 +224,18 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
}
if _, ok := props["attachments"].([]interface{}); ok {
rmsg.Extra["attachments"] = props["attachments"].([]interface{})
if rmsg.Text == "" {
for _, attachment := range rmsg.Extra["attachments"] {
attach := attachment.(map[string]interface{})
if attach["text"].(string) != "" {
rmsg.Text += attach["text"].(string)
continue
}
if attach["fallback"].(string) != "" {
rmsg.Text += attach["fallback"].(string)
}
}
}
}
}

View File

@ -19,14 +19,15 @@ import (
)
type Bslack struct {
mh *matterhook.Client
sc *slack.Client
rtm *slack.RTM
Users []slack.User
Usergroups []slack.UserGroup
si *slack.Info
channels []slack.Channel
uuid string
mh *matterhook.Client
sc *slack.Client
rtm *slack.RTM
Users []slack.User
Usergroups []slack.UserGroup
si *slack.Info
channels []slack.Channel
UseChannelID bool
uuid string
*bridge.Config
sync.RWMutex
}
@ -98,6 +99,20 @@ func (b *Bslack) Disconnect() error {
}
func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
// use ID:channelid and resolve it to the actual name
idcheck := strings.Split(channel.Name, "ID:")
if len(idcheck) > 1 {
b.UseChannelID = true
ch, err := b.sc.GetChannelInfo(idcheck[1])
if err != nil {
return err
}
channel.Name = ch.Name
if err != nil {
return err
}
}
// we can only join channels using the API
if b.sc != nil {
if strings.HasPrefix(b.GetString("Token"), "xoxb") {
@ -131,11 +146,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
return b.sendWebhook(msg)
}
// get the slack channel
schannel, err := b.getChannelByName(msg.Channel)
if err != nil {
return "", err
}
channelID := b.getChannelID(msg.Channel)
// Delete message
if msg.Event == config.EVENT_MSG_DELETE {
@ -145,7 +156,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
}
// we get a "slack <ID>", split it
ts := strings.Fields(msg.ID)
_, _, err := b.sc.DeleteMessage(schannel.ID, ts[1])
_, _, err := b.sc.DeleteMessage(channelID, ts[1])
if err != nil {
return msg.ID, err
}
@ -160,7 +171,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
// Edit message if we have an ID
if msg.ID != "" {
ts := strings.Fields(msg.ID)
_, _, _, err := b.sc.UpdateMessage(schannel.ID, ts[1], msg.Text)
_, _, _, err := b.sc.UpdateMessage(channelID, ts[1], msg.Text)
if err != nil {
return msg.ID, err
}
@ -192,16 +203,16 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
// Upload a file if it exists
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
b.sc.PostMessage(schannel.ID, rmsg.Username+rmsg.Text, np)
b.sc.PostMessage(channelID, rmsg.Username+rmsg.Text, np)
}
// check if we have files to upload (from slack, telegram or mattermost)
if len(msg.Extra["file"]) > 0 {
b.handleUploadFile(&msg, schannel.ID)
b.handleUploadFile(&msg, channelID)
}
}
// Post normal message
_, id, err := b.sc.PostMessage(schannel.ID, msg.Text, np)
_, id, err := b.sc.PostMessage(channelID, msg.Text, np)
if err != nil {
return "", err
}
@ -212,11 +223,11 @@ func (b *Bslack) Reload(cfg *bridge.Config) (string, error) {
return "", nil
}
func (b *Bslack) getAvatar(user string) string {
func (b *Bslack) getAvatar(userid string) string {
var avatar string
if b.Users != nil {
for _, u := range b.Users {
if user == u.Name {
if userid == u.ID {
return u.Profile.Image48
}
}
@ -224,6 +235,7 @@ func (b *Bslack) getAvatar(user string) string {
return avatar
}
/*
func (b *Bslack) getChannelByName(name string) (*slack.Channel, error) {
if b.channels == nil {
return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.Account, name)
@ -235,6 +247,7 @@ func (b *Bslack) getChannelByName(name string) (*slack.Channel, error) {
}
return nil, fmt.Errorf("%s: channel %s not found", b.Account, name)
}
*/
func (b *Bslack) getChannelByID(ID string) (*slack.Channel, error) {
if b.channels == nil {
@ -270,7 +283,7 @@ func (b *Bslack) handleSlack() {
message.Text = html.UnescapeString(message.Text)
// Add the avatar
message.Avatar = b.getAvatar(strings.ToLower(message.Username))
message.Avatar = b.getAvatar(message.UserID)
b.Log.Debugf("<= Message is %#v", message)
b.Remote <- *message
@ -357,7 +370,7 @@ func (b *Bslack) userGroupName(id string) string {
// @see https://api.slack.com/docs/message-formatting#linking_to_channels_and_users
func (b *Bslack) replaceMention(text string) string {
results := regexp.MustCompile(`<@([a-zA-z0-9]+)>`).FindAllStringSubmatch(text, -1)
results := regexp.MustCompile(`<@([a-zA-Z0-9]+)>`).FindAllStringSubmatch(text, -1)
for _, r := range results {
text = strings.Replace(text, "<@"+r[1]+">", "@"+b.userName(r[1]), -1)
}
@ -486,6 +499,10 @@ func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, er
rmsg := config.Message{Text: ev.Text, Channel: channel.Name, Account: b.Account, ID: "slack " + ev.Timestamp, Extra: make(map[string][]interface{})}
if b.UseChannelID {
rmsg.Channel = "ID:" + channel.ID
}
// find the user id and name
if ev.User != "" && ev.SubType != messageDeleted && ev.SubType != "file_comment" {
user, err := b.rtm.GetUserInfo(ev.User)
@ -503,7 +520,10 @@ func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, er
if rmsg.Text == "" {
for _, attach := range ev.Attachments {
if attach.Text != "" {
rmsg.Text = attach.Text
if attach.Title != "" {
rmsg.Text = attach.Title + "\n"
}
rmsg.Text += attach.Text
} else {
rmsg.Text = attach.Fallback
}
@ -679,3 +699,16 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
}
return false
}
func (b *Bslack) getChannelID(name string) string {
idcheck := strings.Split(name, "ID:")
if len(idcheck) > 1 {
return idcheck[1]
}
for _, channel := range b.channels {
if channel.Name == name {
return channel.ID
}
}
return ""
}

View File

@ -2,13 +2,14 @@ package bsshchat
import (
"bufio"
"io"
"strings"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/shazow/ssh-chat/sshd"
log "github.com/sirupsen/logrus"
"io"
"strings"
)
type Bsshchat struct {
@ -120,6 +121,10 @@ func (b *Bsshchat) handleSshChat() error {
wait := true
for {
if b.r.Scan() {
// ignore messages from ourselves
if !strings.Contains(b.r.Text(), "\033[K") {
continue
}
res := strings.Split(stripPrompt(b.r.Text()), ":")
if res[0] == "-> Set theme" {
wait = false

View File

@ -2,8 +2,9 @@ package btelegram
import (
"bytes"
"github.com/russross/blackfriday"
"html"
"github.com/russross/blackfriday"
)
type customHTML struct {

View File

@ -1,6 +1,7 @@
package btelegram
import (
"html"
"regexp"
"strconv"
"strings"
@ -97,6 +98,10 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
if err != nil {
return "", err
}
if strings.ToLower(b.GetString("MessageFormat")) == "htmlnick" {
b.Log.Debug("Using mode HTML - nick only")
msg.Text = html.EscapeString(msg.Text)
}
m := tgbotapi.NewEditMessageText(chatid, msgid, msg.Username+msg.Text)
if b.GetString("MessageFormat") == "HTML" {
b.Log.Debug("Using mode HTML")
@ -106,6 +111,10 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
b.Log.Debug("Using mode markdown")
m.ParseMode = tgbotapi.ModeMarkdown
}
if strings.ToLower(b.GetString("MessageFormat")) == "htmlnick" {
b.Log.Debug("Using mode HTML - nick only")
m.ParseMode = tgbotapi.ModeHTML
}
_, err = b.c.Send(m)
if err != nil {
return "", err
@ -391,13 +400,17 @@ func (b *Btelegram) sendMessage(chatid int64, username, text string) (string, er
m.Text = username + text
if b.GetString("MessageFormat") == "HTML" {
b.Log.Debug("Using mode HTML")
m.Text = username + text
m.ParseMode = tgbotapi.ModeHTML
}
if b.GetString("MessageFormat") == "Markdown" {
b.Log.Debug("Using mode markdown")
m.ParseMode = tgbotapi.ModeMarkdown
}
if strings.ToLower(b.GetString("MessageFormat")) == "htmlnick" {
b.Log.Debug("Using mode HTML - nick only")
m.Text = username + html.EscapeString(text)
m.ParseMode = tgbotapi.ModeHTML
}
res, err := b.c.Send(m)
if err != nil {
return "", err

View File

@ -2,13 +2,15 @@ package bxmpp
import (
"crypto/tls"
"strings"
"time"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/jpillora/backoff"
"github.com/matterbridge/go-xmpp"
"strings"
"time"
"github.com/rs/xid"
)
type Bxmpp struct {
@ -63,11 +65,18 @@ func (b *Bxmpp) Disconnect() error {
}
func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error {
b.xc.JoinMUCNoHistory(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"))
if channel.Options.Key != "" {
b.Log.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name)
b.xc.JoinProtectedMUC(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"), channel.Options.Key, xmpp.NoHistory, 0, nil)
} else {
b.xc.JoinMUCNoHistory(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"))
}
return nil
}
func (b *Bxmpp) Send(msg config.Message) (string, error) {
var msgid = ""
var msgreplaceid = ""
// ignore delete messages
if msg.Event == config.EVENT_MSG_DELETE {
return "", nil
@ -84,12 +93,17 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
}
}
msgid = xid.New().String()
if msg.ID != "" {
msgid = msg.ID
msgreplaceid = msg.ID
}
// Post normal message
_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})
_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text, ID: msgid, ReplaceID: msgreplaceid})
if err != nil {
return "", err
}
return "", nil
return msgid, nil
}
func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
@ -139,6 +153,7 @@ func (b *Bxmpp) xmppKeepAlive() chan bool {
func (b *Bxmpp) handleXMPP() error {
var ok bool
var msgid string
done := b.xmppKeepAlive()
defer close(done)
for {
@ -154,7 +169,11 @@ func (b *Bxmpp) handleXMPP() error {
if b.skipMessage(v) {
continue
}
rmsg := config.Message{Username: b.parseNick(v.Remote), Text: v.Text, Channel: b.parseChannel(v.Remote), Account: b.Account, UserID: v.Remote}
msgid = v.ID
if v.ReplaceID != "" {
msgid = v.ReplaceID
}
rmsg := config.Message{Username: b.parseNick(v.Remote), Text: v.Text, Channel: b.parseChannel(v.Remote), Account: b.Account, UserID: v.Remote, ID: msgid}
// check if we have an action event
rmsg.Text, ok = b.replaceAction(rmsg.Text)
@ -181,6 +200,7 @@ func (b *Bxmpp) replaceAction(text string) (string, bool) {
// handleUploadFile handles native upload of files
func (b *Bxmpp) handleUploadFile(msg *config.Message) (string, error) {
var urldesc = ""
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
if fi.Comment != "" {

View File

@ -1,14 +1,51 @@
# v1.11.1
## New features
* slack: Add support for slack channels by ID. Closes #436
* discord: Clip too long messages sent to discord (discord). Closes #440
## Bugfix
* general: fix possible panic on downloads that are too big #448
* general: Fix avatar uploads to work with MediaDownloadPath. Closes #454
* discord: allow receiving of topic changes/channel leave/joins from other bridges through the webhook
* discord: Add a space before url in file uploads (discord). Closes #461
* discord: Skip empty messages being sent with the webhook (discord). #469
* mattermost: Use nickname instead of username if defined (mattermost). Closes #452
* irc: Stop numbers being stripped after non-color control codes (irc) (#465)
* slack: Use UserID to look for avatar instead of username (slack). Closes #472
# v1.11.0
## New features
* general: Add config option MediaDownloadPath (#443). See `MediaDownloadPath` in matterbridge.toml.sample
* general: Add MediaDownloadBlacklist option. Closes #442. See `MediaDownloadBlacklist` in matterbridge.toml.sample
* xmpp: Add channel password support for XMPP (#451)
* xmpp: Add message correction support for XMPP (#437)
* telegram: Add support for MessageFormat=htmlnick (telegram). #444
* mattermost: Add support for mattermost 5.x
## Enhancements
* slack: Add Title from attachment slack message (#446)
* irc: Prevent white or black color codes (irc) (#434)
## Bugfix
* slack: Fix regexp in replaceMention (slack). (#435)
* irc: Reconnect on quit. (irc) See #431 (#445)
* sshchat: Ignore messages from ourself. (sshchat) Closes #439
# v1.10.1
## New features
* irc: Colorize username sent to IRC using its crc32 IEEE checksum (#423). See `ColorNicks` in matterbridge.toml.sample
* irc: Add support for CJK to/from utf-8 (irc). #400
* telegram: Add QuoteFormat option (telegram). Closes #413. See `QuoteFormat` in matterbridge.toml.sample
* xmpp: Send attached files to XMPP in different message with OOB data and without body (#421)
## Bugfix
* general: updated irc/xmpp/telegram libraries
* mattermost/slack/rocketchat: Fix iconurl regression. Closes #430
* mattermost/slack: Use uuid instead of userid. Fixes #429
* slack: Avatar spoofing from Slack to Discord with uppercase in nick doesn't work (#433)
* irc: Fix format string bug (irc) (#428)
* irc: Colorize username sent to IRC using its crc32 IEEE checksum (#423). See `ColorNicks` in matterbridge.toml.sample
* irc: Add support for CJK to/from utf-8 (irc). #400
* telegram: Add QuoteFormat option (telegram). Closes #41. See `QuoteFormat` in matterbridge.toml.sample
* xmpp: Send attached files to XMPP in different message with OOB data and without body (#421)
# v1.10.0
## New features

View File

@ -3,31 +3,35 @@ package gateway
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/api"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/discord"
"github.com/42wim/matterbridge/bridge/gitter"
"github.com/42wim/matterbridge/bridge/irc"
"github.com/42wim/matterbridge/bridge/matrix"
"github.com/42wim/matterbridge/bridge/mattermost"
"github.com/42wim/matterbridge/bridge/rocketchat"
"github.com/42wim/matterbridge/bridge/slack"
"github.com/42wim/matterbridge/bridge/sshchat"
"github.com/42wim/matterbridge/bridge/steam"
"github.com/42wim/matterbridge/bridge/telegram"
"github.com/42wim/matterbridge/bridge/xmpp"
"github.com/42wim/matterbridge/bridge/zulip"
bdiscord "github.com/42wim/matterbridge/bridge/discord"
bgitter "github.com/42wim/matterbridge/bridge/gitter"
birc "github.com/42wim/matterbridge/bridge/irc"
bmatrix "github.com/42wim/matterbridge/bridge/matrix"
bmattermost "github.com/42wim/matterbridge/bridge/mattermost"
brocketchat "github.com/42wim/matterbridge/bridge/rocketchat"
bslack "github.com/42wim/matterbridge/bridge/slack"
bsshchat "github.com/42wim/matterbridge/bridge/sshchat"
bsteam "github.com/42wim/matterbridge/bridge/steam"
btelegram "github.com/42wim/matterbridge/bridge/telegram"
bxmpp "github.com/42wim/matterbridge/bridge/xmpp"
bzulip "github.com/42wim/matterbridge/bridge/zulip"
"github.com/hashicorp/golang-lru"
log "github.com/sirupsen/logrus"
// "github.com/davecgh/go-spew/spew"
"crypto/sha1"
"github.com/hashicorp/golang-lru"
"github.com/peterhellberg/emojilib"
"net/http"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/peterhellberg/emojilib"
)
type Gateway struct {
@ -411,46 +415,83 @@ func (gw *Gateway) modifyMessage(msg *config.Message) {
}
}
// handleFiles uploads or places all files on the given msg to the MediaServer and
// adds the new URL of the file on the MediaServer onto the given msg.
func (gw *Gateway) handleFiles(msg *config.Message) {
reg := regexp.MustCompile("[^a-zA-Z0-9]+")
// if we don't have a attachfield or we don't have a mediaserver configured return
if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" {
// If we don't have a attachfield or we don't have a mediaserver configured return
if msg.Extra == nil || (gw.Config.General.MediaServerUpload == "" && gw.Config.General.MediaDownloadPath == "") {
return
}
// if we actually have files, start uploading them to the mediaserver
if len(msg.Extra["file"]) > 0 {
client := &http.Client{
Timeout: time.Second * 5,
}
for i, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
ext := filepath.Ext(fi.Name)
fi.Name = fi.Name[0 : len(fi.Name)-len(ext)]
fi.Name = reg.ReplaceAllString(fi.Name, "_")
fi.Name = fi.Name + ext
sha1sum := fmt.Sprintf("%x", sha1.Sum(*fi.Data))
reader := bytes.NewReader(*fi.Data)
// If we don't have files, nothing to upload.
if len(msg.Extra["file"]) == 0 {
return
}
client := &http.Client{
Timeout: time.Second * 5,
}
for i, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
ext := filepath.Ext(fi.Name)
fi.Name = fi.Name[0 : len(fi.Name)-len(ext)]
fi.Name = reg.ReplaceAllString(fi.Name, "_")
fi.Name = fi.Name + ext
sha1sum := fmt.Sprintf("%x", sha1.Sum(*fi.Data))[:8]
if gw.Config.General.MediaServerUpload != "" {
// Use MediaServerUpload. Upload using a PUT HTTP request and basicauth.
url := gw.Config.General.MediaServerUpload + "/" + sha1sum + "/" + fi.Name
durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
extra := msg.Extra["file"][i].(config.FileInfo)
extra.URL = durl
req, err := http.NewRequest("PUT", url, reader)
req, err := http.NewRequest("PUT", url, bytes.NewReader(*fi.Data))
if err != nil {
flog.Errorf("mediaserver upload failed: %#v", err)
flog.Errorf("mediaserver upload failed, could not create request: %#v", err)
continue
}
flog.Debugf("mediaserver upload url: %s", url)
req.Header.Set("Content-Type", "binary/octet-stream")
_, err = client.Do(req)
if err != nil {
flog.Errorf("mediaserver upload failed: %#v", err)
flog.Errorf("mediaserver upload failed, could not Do request: %#v", err)
continue
}
} else {
// Use MediaServerPath. Place the file on the current filesystem.
dir := gw.Config.General.MediaDownloadPath + "/" + sha1sum
err := os.Mkdir(dir, os.ModePerm)
if err != nil && !os.IsExist(err) {
flog.Errorf("mediaserver path failed, could not mkdir: %s %#v", err, err)
continue
}
path := dir + "/" + fi.Name
flog.Debugf("mediaserver path placing file: %s", path)
err = ioutil.WriteFile(path, *fi.Data, os.ModePerm)
if err != nil {
flog.Errorf("mediaserver path failed, could not writefile: %s %#v", err, err)
continue
}
flog.Debugf("mediaserver download URL = %s", durl)
// we uploaded the file successfully. Add the SHA
extra.SHA = sha1sum
msg.Extra["file"][i] = extra
}
// Download URL.
durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
flog.Debugf("mediaserver download URL = %s", durl)
// We uploaded/placed the file successfully. Add the SHA and URL.
extra := msg.Extra["file"][i].(config.FileInfo)
extra.URL = durl
extra.SHA = sha1sum
msg.Extra["file"][i] = extra
}
}

View File

@ -2,9 +2,10 @@ package gateway
import (
"fmt"
"strconv"
"github.com/42wim/matterbridge/bridge/config"
"github.com/stretchr/testify/assert"
"strconv"
"testing"
)

View File

@ -2,9 +2,10 @@ package gateway
import (
"fmt"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/gateway/samechannel"
samechannelgateway "github.com/42wim/matterbridge/gateway/samechannel"
// "github.com/davecgh/go-spew/spew"
"time"
)

View File

@ -2,6 +2,7 @@ package samechannelgateway
import (
"fmt"
"github.com/42wim/matterbridge/bridge/config"
"github.com/BurntSushi/toml"
"github.com/stretchr/testify/assert"

View File

@ -3,17 +3,18 @@ package main
import (
"flag"
"fmt"
"os"
"strings"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/gateway"
"github.com/google/gops/agent"
log "github.com/sirupsen/logrus"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
"os"
"strings"
)
var (
version = "1.10.1"
version = "1.11.1"
githash string
)

View File

@ -799,8 +799,10 @@ Token="Yourtokenhere"
## Settings below can be reloaded by editing the file
#OPTIONAL (default empty)
#Only supported format is "HTML", messages will be sent in html parsemode.
#Supported formats are "HTML", "Markdown" and "HTMLNick"
#See https://core.telegram.org/bots/api#html-style
#See https://core.telegram.org/bots/api#markdown-style
#HTMLNick only allows HTML for the nick, the message itself will be html-escaped
MessageFormat=""
#If enabled use the "First Name" as username. If this is empty use the Username
@ -1305,10 +1307,13 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
StripNick=false
#MediaServerUpload and MediaServerDownload are used for uploading images/files/video to
#a remote "mediaserver" (a webserver like caddy for example).
#When configured images/files uploaded on bridges like mattermost,slack, telegram will be downloaded
#and uploaded again to MediaServerUpload URL
#MediaServerUpload (or MediaDownloadPath) and MediaServerDownload are used for uploading
#images/files/video to a remote "mediaserver" (a webserver like caddy for example).
#When configured images/files uploaded on bridges like mattermost, slack, telegram will be
#downloaded and uploaded again to MediaServerUpload URL
#MediaDownloadPath is the filesystem path where the media file will be placed, instead of uploaded,
#for if Matterbridge has write access to the directory your webserver is serving.
#It is an alternative to MediaServerUpload.
#The MediaServerDownload will be used so that bridges without native uploading support:
#gitter, irc and xmpp will be shown links to the files on MediaServerDownload
#
@ -1316,6 +1321,8 @@ StripNick=false
#OPTIONAL (default empty)
MediaServerUpload="https://user:pass@yourserver.com/upload"
#OPTIONAL (default empty)
MediaDownloadPath="/srv/http/yourserver.com/public/download"
#OPTIONAL (default empty)
MediaServerDownload="https://youserver.com/download"
#MediaDownloadSize is the maximum size of attachments, videos, images
@ -1325,9 +1332,15 @@ MediaServerDownload="https://youserver.com/download"
#It will only download from bridges that don't have public links available, which are for the moment
#slack, telegram, matrix and mattermost
#
#Optional (default 1000000 (1 megabyte))
#OPTIONAL (default 1000000 (1 megabyte))
MediaDownloadSize=1000000
#MediaDownloadBlacklist allows you to blacklist specific files from being downloaded.
#Filenames matching these regexp will not be download/uploaded to the mediaserver
#You can use regex for this, see https://regex-golang.appspot.com/assets/html/index.html for more regex info
#OPTIONAL (default empty)
MediaDownloadBlacklist=[".html$",".htm$"]
###################################################################
#Gateway configuration
###################################################################
@ -1364,6 +1377,7 @@ enable=true
#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)
# (https://github.com/42wim/matterbridge/issues/57)
@ -1380,9 +1394,9 @@ enable=true
#REQUIRED
channel="#testing"
#OPTIONAL - only used for IRC protocol at the moment
#OPTIONAL - only used for IRC and XMPP protocols at the moment
[gateway.in.options]
#OPTIONAL - your irc channel key
#OPTIONAL - your irc / xmpp channel key
key="yourkey"
@ -1391,9 +1405,9 @@ enable=true
account="irc.freenode"
channel="#testing"
#OPTIONAL - only used for IRC protocol at the moment
#OPTIONAL - only used for IRC and XMPP protocols at the moment
[gateway.out.options]
#OPTIONAL - your irc channel key
#OPTIONAL - your irc / xmpp channel key
key="yourkey"
#[[gateway.inout]] can be used when then channel will be used to receive from
@ -1402,9 +1416,9 @@ enable=true
account="mattermost.work"
channel="off-topic"
#OPTIONAL - only used for IRC protocol at the moment
#OPTIONAL - only used for IRC and XMPP protocols at the moment
[gateway.inout.options]
#OPTIONAL - your irc channel key
#OPTIONAL - your irc / xmpp channel key
key="yourkey"
[[gateway.inout]]

View File

@ -310,6 +310,11 @@ func (m *MMClient) parseMessage(rmsg *Message) {
switch rmsg.Raw.Event {
case model.WEBSOCKET_EVENT_POSTED, model.WEBSOCKET_EVENT_POST_EDITED, model.WEBSOCKET_EVENT_POST_DELETED:
m.parseActionPost(rmsg)
case "user_updated":
user := rmsg.Raw.Data["user"].(map[string]interface{})
if _, ok := user["id"].(string); ok {
m.UpdateUser(user["id"].(string))
}
/*
case model.ACTION_USER_REMOVED:
m.handleWsActionUserRemoved(&rmsg)
@ -750,6 +755,16 @@ func (m *MMClient) GetUser(userId string) *model.User {
return m.Users[userId]
}
func (m *MMClient) UpdateUser(userId string) {
m.Lock()
defer m.Unlock()
res, resp := m.Client.GetUser(userId, "")
if resp.Error != nil {
return
}
m.Users[userId] = res
}
func (m *MMClient) GetUserName(userId string) string {
user := m.GetUser(userId)
if user != nil {
@ -758,6 +773,14 @@ func (m *MMClient) GetUserName(userId string) string {
return ""
}
func (m *MMClient) GetNickName(userId string) string {
user := m.GetUser(userId)
if user != nil {
return user.Nickname
}
return ""
}
func (m *MMClient) GetStatus(userId string) string {
res, resp := m.Client.GetUserStatus(userId, "")
if resp.Error != nil {
@ -917,7 +940,8 @@ func supportedVersion(version string) bool {
if strings.HasPrefix(version, "3.8.0") ||
strings.HasPrefix(version, "3.9.0") ||
strings.HasPrefix(version, "3.10.0") ||
strings.HasPrefix(version, "4.") {
strings.HasPrefix(version, "4.") ||
strings.HasPrefix(version, "5.") {
return true
}
return false

View File

@ -6,14 +6,15 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"github.com/gorilla/schema"
"github.com/nlopes/slack"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"time"
"github.com/gorilla/schema"
"github.com/nlopes/slack"
)
// OMessage for mattermost incoming webhook. (send to mattermost)

View File

@ -575,6 +575,8 @@ type Chat struct {
Thread string
Ooburl string
Oobdesc string
ID string
ReplaceID string
Roster Roster
Other []string
OtherElem []XMLElement
@ -626,6 +628,8 @@ func (c *Client) Recv() (stanza interface{}, err error) {
Text: v.Body,
Subject: v.Subject,
Thread: v.Thread,
ID: v.ID,
ReplaceID: v.ReplaceID.ID,
Other: v.OtherStrings(),
OtherElem: v.Other,
Stamp: stamp,
@ -657,6 +661,8 @@ func (c *Client) Send(chat Chat) (n int, err error) {
var subtext = ``
var thdtext = ``
var oobtext = ``
var msgidtext = ``
var msgcorrecttext = ``
if chat.Subject != `` {
subtext = `<subject>` + xmlEscape(chat.Subject) + `</subject>`
}
@ -670,7 +676,13 @@ func (c *Client) Send(chat Chat) (n int, err error) {
}
oobtext += `</x>`
}
return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>" + subtext + "<body>%s</body>" + oobtext + thdtext + "</message>",
if chat.ID != `` {
msgidtext = `id='` + xmlEscape(chat.ID) + `'`
}
if chat.ReplaceID != `` {
msgcorrecttext = `<replace id='` + xmlEscape(chat.ReplaceID) + `' xmlns='urn:xmpp:message-correct:0'/>`
}
return fmt.Fprintf(c.conn, "<message to='%s' type='%s' " + msgidtext + " xml:lang='en'>" + subtext + "<body>%s</body>" + msgcorrecttext + oobtext + thdtext + "</message>",
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
}
@ -787,6 +799,11 @@ type bindBind struct {
Jid string `xml:"jid"`
}
type clientMessageCorrect struct {
XMLName xml.Name `xml:"urn:xmpp:message-correct:0 replace"`
ID string `xml:"id,attr"`
}
// RFC 3921 B.1 jabber:client
type clientMessage struct {
XMLName xml.Name `xml:"jabber:client message"`
@ -799,6 +816,7 @@ type clientMessage struct {
Subject string `xml:"subject"`
Body string `xml:"body"`
Thread string `xml:"thread"`
ReplaceID clientMessageCorrect
// Any hasn't matched element
Other []XMLElement `xml:",any"`

4
vendor/manifest vendored
View File

@ -402,7 +402,7 @@
"importpath": "github.com/matterbridge/go-xmpp",
"repository": "https://github.com/matterbridge/go-xmpp",
"vcs": "git",
"revision": "3cceb718219057817b66c4bb55673f5f417c0bff",
"revision": "cd19799fba91790ac733655c546bce2107e5b838",
"branch": "work",
"notests": true
},
@ -601,7 +601,7 @@
"repository": "https://github.com/nlopes/slack",
"vcs": "git",
"revision": "107290b5bbaf3e634833346bb4ff389b1c782bc7",
"branch": "master",
"branch": "HEAD",
"notests": true
},
{