Compare commits

..

9 Commits

Author SHA1 Message Date
Wim
6ea8be5749 Release v0.11.0 2017-04-11 21:51:23 +02:00
Wim
36024d5439 Add 3.8.0 support (mattermost) 2017-04-09 23:15:11 +02:00
Wim
8d52c98373 Update README 2017-04-08 00:57:11 +02:00
Wim
b4a4eb0057 Update changelog 2017-04-08 00:50:17 +02:00
Wim
b469c8ddbd Rejoin channel when kicked (irc). Closes #146 2017-04-08 00:42:37 +02:00
Wim
eee0036c7f Modify iconurl correctly (mattermost). Closes #145 2017-04-08 00:16:46 +02:00
Wim
89c66b9430 Reconnect on session removal (mattermost) 2017-04-07 23:27:41 +02:00
Wim
bd38319d83 Add support for showing/hiding join/leave messages from mattermost. Closes #147 2017-04-07 22:27:36 +02:00
Wim
33dffd5ea8 Fix join/leave regression (irc) 2017-04-03 22:18:29 +02:00
9 changed files with 141 additions and 33 deletions

View File

@@ -28,7 +28,7 @@ Simple bridge between Mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, R
# Requirements
Accounts to one of the supported bridges
* [Mattermost](https://github.com/mattermost/platform/) 3.5.x - 3.7.x
* [Mattermost](https://github.com/mattermost/platform/) 3.5.x - 3.8.x
* [IRC](http://www.mirc.com/servers.html)
* [XMPP](https://jabber.org)
* [Gitter](https://gitter.im)
@@ -42,7 +42,7 @@ Accounts to one of the supported bridges
# Installing
## Binaries
Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/)
* Latest release [v0.10.3](https://github.com/42wim/matterbridge/releases/latest)
* Latest stable release [v0.11.0](https://github.com/42wim/matterbridge/releases/latest)
## Building
Go 1.6+ 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)

View File

@@ -10,8 +10,9 @@ import (
)
const (
EVENT_JOIN_LEAVE = "join_leave"
EVENT_FAILURE = "failure"
EVENT_JOIN_LEAVE = "join_leave"
EVENT_FAILURE = "failure"
EVENT_REJOIN_CHANNELS = "rejoin_channels"
)
type Message struct {

View File

@@ -167,14 +167,19 @@ func (b *Birc) handleNewConnection(event *irc.Event) {
i.AddCallback("JOIN", b.handleJoinPart)
i.AddCallback("PART", b.handleJoinPart)
i.AddCallback("QUIT", b.handleJoinPart)
i.AddCallback("KICK", b.handleJoinPart)
i.AddCallback("*", b.handleOther)
// we are now fully connected
b.connected <- struct{}{}
}
func (b *Birc) handleJoinPart(event *irc.Event) {
flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
channel := event.Arguments[0]
if event.Code == "KICK" {
flog.Infof("Got kicked from %s by %s", channel, event.Nick)
b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
return
}
if event.Code == "QUIT" {
if event.Nick == b.Nick && strings.Contains(event.Raw, "Ping timeout") {
flog.Infof("%s reconnecting ..", b.Account)
@@ -182,6 +187,7 @@ func (b *Birc) handleJoinPart(event *irc.Event) {
return
}
}
flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
b.Remote <- config.Message{Username: "system", Text: event.Nick + " " + strings.ToLower(event.Code) + "s", Channel: channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE}
flog.Debugf("handle %#v", event)
}

View File

@@ -72,6 +72,7 @@ func (b *Bmattermost) Connect() error {
flog.Info("Connection succeeded")
b.TeamId = b.mc.GetTeamId()
go b.mc.WsReceiver()
go b.mc.StatusLoop()
}
go b.handleMatter()
return nil
@@ -100,6 +101,7 @@ func (b *Bmattermost) Send(msg config.Message) error {
}
if !b.Config.UseAPI {
matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
matterMessage.IconURL = msg.Avatar
matterMessage.Channel = channel
matterMessage.UserName = nick
matterMessage.Type = ""
@@ -131,6 +133,14 @@ func (b *Bmattermost) handleMatter() {
func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
for message := range b.mc.MessageChan {
flog.Debugf("%#v", message.Raw.Data)
if message.Type == "system_join_leave" ||
message.Type == "system_join_channel" ||
message.Type == "system_leave_channel" {
flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
b.Remote <- config.Message{Username: "system", Text: message.Text, Channel: message.Channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE}
continue
}
// do not post our own messages back to irc
// only listen to message from our team
if message.Raw.Event == "posted" && b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId {

View File

@@ -1,8 +1,17 @@
# v0.11.0-dev
# v0.11.0
## New features
* general: reusing the same account on multiple gateways now also reuses the connection.
* general: reusing the same account on multiple gateways now also reuses the connection.
This is particuarly useful for irc. See #87
* general: the Name is now REQUIRED and needs to be UNIQUE for each gateway configuration
* telegram: Support edited messages (telegram). See #141
* mattermost: Add support for showing/hiding join/leave messages from mattermost. Closes #147
* mattermost: Reconnect on session removal/timeout (mattermost)
* irc: Rejoin channel when kicked (irc).
## Bugfix
* mattermost: Remove space after nick (mattermost). Closes #142
* mattermost: Modify iconurl correctly (mattermost).
* irc: Fix join/leave regression (irc)
# v0.10.3
## Bugfix

View File

@@ -103,6 +103,15 @@ func (gw *Gateway) handleReceive() {
}
}
}
if msg.Event == config.EVENT_REJOIN_CHANNELS {
for _, br := range gw.Bridges {
if msg.Account == br.Account {
br.Joined = make(map[string]bool)
br.JoinChannels()
}
}
continue
}
if !gw.ignoreMessage(&msg) {
msg.Timestamp = time.Now()
for _, br := range gw.Bridges {
@@ -173,6 +182,10 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con
}
func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
// only relay join/part when configged
if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
return
}
// broadcast to every out channel (irc QUIT)
if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
log.Debug("empty channel")
@@ -186,6 +199,7 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
}
log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
msg.Channel = channel.Name
gw.modifyAvatar(&msg, dest)
gw.modifyUsername(&msg, dest)
// for api we need originchannel as channel
if dest.Protocol == "api" {
@@ -225,6 +239,17 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) {
msg.Username = nick
}
func (gw *Gateway) modifyAvatar(msg *config.Message, dest *bridge.Bridge) {
iconurl := gw.Config.General.IconURL
if iconurl == "" {
iconurl = dest.Config.IconURL
}
iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
if msg.Avatar == "" {
msg.Avatar = iconurl
}
}
func getChannelID(msg config.Message) string {
return msg.Channel + msg.Account
}

View File

@@ -12,7 +12,7 @@ import (
)
var (
version = "0.11.0-dev"
version = "0.11.0"
githash string
)

View File

@@ -64,7 +64,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
#Enable to show users joins/parts from other bridges
#Only works hiding/show messages from irc and mattermost bridge for now
#OPTIONAL (default false)
ShowJoinPart=false
@@ -114,7 +115,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
#Enable to show users joins/parts from other bridges
#Only works hiding/show messages from irc and mattermost bridge for now
#OPTIONAL (default false)
ShowJoinPart=false
@@ -157,7 +159,8 @@ IgnoreNicks="spammer1 spammer2"
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
#Enable to show users joins/parts from other bridges
#Only works hiding/show messages from irc and mattermost bridge for now
#OPTIONAL (default false)
ShowJoinPart=false
@@ -250,7 +253,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
#Enable to show users joins/parts from other bridges
#Only works hiding/show messages from irc and mattermost bridge for now
#OPTIONAL (default false)
ShowJoinPart=false
@@ -282,7 +286,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
#Enable to show users joins/parts from other bridges
#Only works hiding/show messages from irc and mattermost bridge for now
#OPTIONAL (default false)
ShowJoinPart=false
@@ -362,7 +367,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
#Enable to show users joins/parts from other bridges
#Only works hiding/show messages from irc and mattermost bridge for now
#OPTIONAL (default false)
ShowJoinPart=false
@@ -397,7 +403,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
#Enable to show users joins/parts from other bridges
#Only works hiding/show messages from irc and mattermost bridge for now
#OPTIONAL (default false)
ShowJoinPart=false
@@ -432,7 +439,8 @@ IgnoreNicks="spammer1 spammer2"
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
#Enable to show users joins/parts from other bridges
#Only works hiding/show messages from irc and mattermost bridge for now
#OPTIONAL (default false)
ShowJoinPart=false
@@ -489,7 +497,8 @@ IgnoreNicks="ircspammer1 ircspammer2"
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
#Enable to show users joins/parts from other bridges
#Only works hiding/show messages from irc and mattermost bridge for now
#OPTIONAL (default false)
ShowJoinPart=false
@@ -532,7 +541,8 @@ IgnoreNicks="spammer1 spammer2"
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
#Enable to show users joins/parts from other bridges
#Only works hiding/show messages from irc and mattermost bridge for now
#OPTIONAL (default false)
ShowJoinPart=false

View File

@@ -4,9 +4,11 @@ import (
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/http/cookiejar"
"net/url"
"strconv"
"strings"
"sync"
"time"
@@ -34,6 +36,7 @@ type Message struct {
Channel string
Username string
Text string
Type string
}
type Team struct {
@@ -47,19 +50,20 @@ type Team struct {
type MMClient struct {
sync.RWMutex
*Credentials
Team *Team
OtherTeams []*Team
Client *model.Client
User *model.User
Users map[string]*model.User
MessageChan chan *Message
log *log.Entry
WsClient *websocket.Conn
WsQuit bool
WsAway bool
WsConnected bool
WsSequence int64
WsPingChan chan *model.WebSocketResponse
Team *Team
OtherTeams []*Team
Client *model.Client
User *model.User
Users map[string]*model.User
MessageChan chan *Message
log *log.Entry
WsClient *websocket.Conn
WsQuit bool
WsAway bool
WsConnected bool
WsSequence int64
WsPingChan chan *model.WebSocketResponse
ServerVersion string
}
func New(login, pass, team, server string) *MMClient {
@@ -104,6 +108,14 @@ func (m *MMClient) Login() error {
m.Client = model.NewClient(uriScheme + m.Credentials.Server)
m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
m.Client.HttpClient.Timeout = time.Second * 10
// bogus call to get the serverversion
m.Client.GetClientProperties()
if firstConnection && !supportedVersion(m.Client.ServerVersion) {
return fmt.Errorf("unsupported mattermost version: %s", m.Client.ServerVersion)
}
m.ServerVersion = m.Client.ServerVersion
m.log.Infof("Found version %s", m.ServerVersion)
var myinfo *model.Result
var appErr *model.AppError
var logmsg = "trying login"
@@ -177,6 +189,7 @@ func (m *MMClient) Login() error {
}
b.Reset()
m.log.Debug("WsClient: connected")
m.WsSequence = 1
m.WsPingChan = make(chan *model.WebSocketResponse)
// only start to parse WS messages when login is completely done
@@ -266,6 +279,7 @@ func (m *MMClient) parseActionPost(rmsg *Message) {
}
rmsg.Username = m.GetUser(data.UserId).Username
rmsg.Channel = m.GetChannelName(data.ChannelId)
rmsg.Type = data.Type
rmsg.Team = m.GetTeamName(rmsg.Raw.Data["team_id"].(string))
// direct message
if rmsg.Raw.Data["channel_type"] == "D" {
@@ -292,7 +306,12 @@ func (m *MMClient) UpdateChannels() error {
if err != nil {
return errors.New(err.DetailedError)
}
mmchannels2, err := m.Client.GetMoreChannels("")
var mmchannels2 *model.Result
if m.mmVersion() >= 3.8 {
mmchannels2, err = m.Client.GetMoreChannelsPage(0, 5000)
} else {
mmchannels2, err = m.Client.GetMoreChannels("")
}
if err != nil {
return errors.New(err.DetailedError)
}
@@ -427,6 +446,14 @@ func (m *MMClient) UpdateChannelHeader(channelId string, header string) {
func (m *MMClient) UpdateLastViewed(channelId string) {
m.log.Debugf("posting lastview %#v", channelId)
if m.mmVersion() >= 3.8 {
view := model.ChannelView{ChannelId: channelId}
res, _ := m.Client.ViewChannel(view)
if res == false {
m.log.Errorf("ChannelView update for %s failed", channelId)
}
return
}
_, err := m.Client.UpdateLastViewedAt(channelId, true)
if err != nil {
m.log.Error(err)
@@ -628,6 +655,7 @@ func (m *MMClient) StatusLoop() {
m.Logout()
m.WsQuit = false
m.Login()
go m.WsReceiver()
}
}
time.Sleep(time.Second * 60)
@@ -659,7 +687,11 @@ func (m *MMClient) initUser() error {
return errors.New(err.DetailedError)
}
t.Channels = mmchannels.Data.(*model.ChannelList)
mmchannels, err = m.Client.GetMoreChannels("")
if m.mmVersion() >= 3.8 {
mmchannels, err = m.Client.GetMoreChannelsPage(0, 5000)
} else {
mmchannels, err = m.Client.GetMoreChannels("")
}
if err != nil {
return errors.New(err.DetailedError)
}
@@ -687,3 +719,18 @@ func (m *MMClient) sendWSRequest(action string, data map[string]interface{}) err
m.WsClient.WriteJSON(req)
return nil
}
func (m *MMClient) mmVersion() float64 {
v, _ := strconv.ParseFloat(m.ServerVersion[0:3], 64)
return v
}
func supportedVersion(version string) bool {
if strings.HasPrefix(version, "3.5.0") ||
strings.HasPrefix(version, "3.6.0") ||
strings.HasPrefix(version, "3.7.0") ||
strings.HasPrefix(version, "3.8.0") {
return true
}
return false
}