Compare commits
	
		
			15 Commits
		
	
	
		
			v0.10.2
			...
			v0.11.0-be
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | b4a4eb0057 | ||
|   | b469c8ddbd | ||
|   | eee0036c7f | ||
|   | 89c66b9430 | ||
|   | bd38319d83 | ||
|   | 33dffd5ea8 | ||
|   | 57176dadd4 | ||
|   | dd449a8705 | ||
|   | 587ad9f41d | ||
|   | a16ad8bf3b | ||
|   | 1e0490bd36 | ||
|   | 8afc641f0c | ||
|   | 2e4d58cb92 | ||
|   | 02d7e2db65 | ||
|   | f935c573e9 | 
| @@ -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.2](https://github.com/42wim/matterbridge/releases/latest) | ||||
| * Latest release [v0.10.3](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) | ||||
|   | ||||
| @@ -27,23 +27,23 @@ type Bridger interface { | ||||
| type Bridge struct { | ||||
| 	Config config.Protocol | ||||
| 	Bridger | ||||
| 	Name        string | ||||
| 	Account     string | ||||
| 	Protocol    string | ||||
| 	ChannelsIn  map[string]config.ChannelOptions | ||||
| 	ChannelsOut map[string]config.ChannelOptions | ||||
| 	Name     string | ||||
| 	Account  string | ||||
| 	Protocol string | ||||
| 	Channels map[string]config.ChannelInfo | ||||
| 	Joined   map[string]bool | ||||
| } | ||||
|  | ||||
| func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Bridge { | ||||
| 	b := new(Bridge) | ||||
| 	b.ChannelsIn = make(map[string]config.ChannelOptions) | ||||
| 	b.ChannelsOut = make(map[string]config.ChannelOptions) | ||||
| 	b.Channels = make(map[string]config.ChannelInfo) | ||||
| 	accInfo := strings.Split(bridge.Account, ".") | ||||
| 	protocol := accInfo[0] | ||||
| 	name := accInfo[1] | ||||
| 	b.Name = name | ||||
| 	b.Protocol = protocol | ||||
| 	b.Account = bridge.Account | ||||
| 	b.Joined = make(map[string]bool) | ||||
|  | ||||
| 	// override config from environment | ||||
| 	config.OverrideCfgFromEnv(cfg, protocol, name) | ||||
| @@ -83,33 +83,28 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Brid | ||||
| } | ||||
|  | ||||
| func (b *Bridge) JoinChannels() error { | ||||
| 	exists := make(map[string]bool) | ||||
| 	err := b.joinChannels(b.ChannelsIn, exists) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = b.joinChannels(b.ChannelsOut, exists) | ||||
| 	err := b.joinChannels(b.Channels, b.Joined) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bridge) joinChannels(cMap map[string]config.ChannelOptions, exists map[string]bool) error { | ||||
| func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map[string]bool) error { | ||||
| 	mychannel := "" | ||||
| 	for channel, info := range cMap { | ||||
| 		if !exists[channel] { | ||||
| 			mychannel = channel | ||||
| 			log.Infof("%s: joining %s", b.Account, channel) | ||||
| 			if b.Protocol == "irc" && info.Key != "" { | ||||
| 				log.Debugf("using key %s for channel %s", info.Key, channel) | ||||
| 				mychannel = mychannel + " " + info.Key | ||||
| 	for ID, channel := range channels { | ||||
| 		if !exists[ID] { | ||||
| 			mychannel = channel.Name | ||||
| 			log.Infof("%s: joining %s (%s)", b.Account, channel.Name, ID) | ||||
| 			if b.Protocol == "irc" && channel.Options.Key != "" { | ||||
| 				log.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name) | ||||
| 				mychannel = mychannel + " " + channel.Options.Key | ||||
| 			} | ||||
| 			err := b.JoinChannel(mychannel) | ||||
| 			err := b.JoinChannel(channel.Name) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			exists[channel] = true | ||||
| 			exists[ID] = true | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
|   | ||||
| @@ -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 { | ||||
| @@ -25,6 +26,16 @@ type Message struct { | ||||
| 	Timestamp time.Time | ||||
| } | ||||
|  | ||||
| type ChannelInfo struct { | ||||
| 	Name        string | ||||
| 	Account     string | ||||
| 	Direction   string | ||||
| 	ID          string | ||||
| 	GID         map[string]bool | ||||
| 	SameChannel map[string]bool | ||||
| 	Options     ChannelOptions | ||||
| } | ||||
|  | ||||
| type Protocol struct { | ||||
| 	BindAddress            string // mattermost, slack | ||||
| 	Buffer                 int    // api | ||||
| @@ -63,9 +74,10 @@ type ChannelOptions struct { | ||||
| } | ||||
|  | ||||
| type Bridge struct { | ||||
| 	Account string | ||||
| 	Channel string | ||||
| 	Options ChannelOptions | ||||
| 	Account     string | ||||
| 	Channel     string | ||||
| 	Options     ChannelOptions | ||||
| 	SameChannel bool | ||||
| } | ||||
|  | ||||
| type Gateway struct { | ||||
|   | ||||
| @@ -92,7 +92,7 @@ func (b *Birc) Connect() error { | ||||
| } | ||||
|  | ||||
| func (b *Birc) Disconnect() error { | ||||
| 	b.i.Disconnect() | ||||
| 	//b.i.Disconnect() | ||||
| 	close(b.Local) | ||||
| 	return nil | ||||
| } | ||||
| @@ -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) | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| @@ -96,15 +97,11 @@ func (b *Bmattermost) Send(msg config.Message) error { | ||||
| 	channel := msg.Channel | ||||
|  | ||||
| 	if b.Config.PrefixMessagesWithNick { | ||||
| 		/*if IsMarkup(message) { | ||||
| 			message = nick + "\n\n" + message | ||||
| 		} else { | ||||
| 		*/ | ||||
| 		message = nick + " " + message | ||||
| 		//} | ||||
| 		message = nick + message | ||||
| 	} | ||||
| 	if !b.Config.UseAPI { | ||||
| 		matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL} | ||||
| 		matterMessage.IconURL = msg.Avatar | ||||
| 		matterMessage.Channel = channel | ||||
| 		matterMessage.UserName = nick | ||||
| 		matterMessage.Type = "" | ||||
| @@ -136,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 { | ||||
|   | ||||
| @@ -73,6 +73,10 @@ func (b *Bslack) Disconnect() error { | ||||
| func (b *Bslack) JoinChannel(channel string) error { | ||||
| 	// we can only join channels using the API | ||||
| 	if b.Config.UseAPI { | ||||
| 		if strings.HasPrefix(b.Config.Token, "xoxb") { | ||||
| 			// TODO check if bot has already joined channel | ||||
| 			return nil | ||||
| 		} | ||||
| 		_, err := b.sc.JoinChannel(channel) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
|   | ||||
| @@ -80,28 +80,30 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | ||||
| 	text := "" | ||||
| 	channel := "" | ||||
| 	for update := range updates { | ||||
| 		var message *tgbotapi.Message | ||||
| 		// handle channels | ||||
| 		if update.ChannelPost != nil { | ||||
| 			if update.ChannelPost.From != nil { | ||||
| 				username = update.ChannelPost.From.FirstName | ||||
| 				if username == "" { | ||||
| 					username = update.ChannelPost.From.UserName | ||||
| 				} | ||||
| 			} | ||||
| 			text = update.ChannelPost.Text | ||||
| 			channel = strconv.FormatInt(update.ChannelPost.Chat.ID, 10) | ||||
| 			message = update.ChannelPost | ||||
| 		} | ||||
| 		if update.EditedChannelPost != nil { | ||||
| 			message = update.EditedChannelPost | ||||
| 		} | ||||
| 		// handle groups | ||||
| 		if update.Message != nil { | ||||
| 			if update.Message.From != nil { | ||||
| 				username = update.Message.From.FirstName | ||||
| 				if username == "" { | ||||
| 					username = update.Message.From.UserName | ||||
| 				} | ||||
| 			} | ||||
| 			text = update.Message.Text | ||||
| 			channel = strconv.FormatInt(update.Message.Chat.ID, 10) | ||||
| 			message = update.Message | ||||
| 		} | ||||
| 		if update.EditedMessage != nil { | ||||
| 			message = update.EditedMessage | ||||
| 		} | ||||
| 		if message.From != nil { | ||||
| 			username = message.From.FirstName | ||||
| 			if username == "" { | ||||
| 				username = message.From.UserName | ||||
| 			} | ||||
| 			text = message.Text | ||||
| 			channel = strconv.FormatInt(message.Chat.ID, 10) | ||||
| 		} | ||||
|  | ||||
| 		if username == "" { | ||||
| 			username = "unknown" | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										19
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,3 +1,22 @@ | ||||
| # v0.11.0-dev | ||||
| ## New features | ||||
| * 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 | ||||
| * slack: Allow bot tokens for now without warning (slack). Closes #140 (fixes user_is_bot message on channel join) | ||||
|  | ||||
| # v0.10.2 | ||||
| ## New features | ||||
| * general: gops agent added. Allows for more debugging. See #134 | ||||
|   | ||||
| @@ -5,7 +5,6 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
| @@ -14,21 +13,21 @@ type Gateway struct { | ||||
| 	*config.Config | ||||
| 	MyConfig        *config.Gateway | ||||
| 	Bridges         map[string]*bridge.Bridge | ||||
| 	ChannelsOut     map[string][]string | ||||
| 	ChannelsIn      map[string][]string | ||||
| 	Channels        map[string]*config.ChannelInfo | ||||
| 	ChannelOptions  map[string]config.ChannelOptions | ||||
| 	Names           map[string]bool | ||||
| 	Name            string | ||||
| 	Message         chan config.Message | ||||
| 	DestChannelFunc func(msg *config.Message, dest string) []string | ||||
| 	DestChannelFunc func(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo | ||||
| } | ||||
|  | ||||
| func New(cfg *config.Config, gateway *config.Gateway) *Gateway { | ||||
| func New(cfg *config.Config) *Gateway { | ||||
| 	gw := &Gateway{} | ||||
| 	gw.Name = gateway.Name | ||||
| 	gw.Config = cfg | ||||
| 	gw.MyConfig = gateway | ||||
| 	gw.Channels = make(map[string]*config.ChannelInfo) | ||||
| 	gw.Message = make(chan config.Message) | ||||
| 	gw.Bridges = make(map[string]*bridge.Bridge) | ||||
| 	gw.Names = make(map[string]bool) | ||||
| 	gw.DestChannelFunc = gw.getDestChannel | ||||
| 	return gw | ||||
| } | ||||
| @@ -36,13 +35,17 @@ func New(cfg *config.Config, gateway *config.Gateway) *Gateway { | ||||
| func (gw *Gateway) AddBridge(cfg *config.Bridge) error { | ||||
| 	for _, br := range gw.Bridges { | ||||
| 		if br.Account == cfg.Account { | ||||
| 			gw.mapChannelsToBridge(br) | ||||
| 			err := br.JoinChannels() | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err) | ||||
| 			} | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	log.Infof("Starting bridge: %s ", cfg.Account) | ||||
| 	br := bridge.New(gw.Config, cfg, gw.Message) | ||||
| 	gw.mapChannelsToBridge(br, gw.ChannelsOut) | ||||
| 	gw.mapChannelsToBridge(br, gw.ChannelsIn) | ||||
| 	gw.mapChannelsToBridge(br) | ||||
| 	gw.Bridges[cfg.Account] = br | ||||
| 	err := br.Connect() | ||||
| 	if err != nil { | ||||
| @@ -55,17 +58,17 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge, cMap map[string][]string) { | ||||
| 	for _, channel := range cMap[br.Account] { | ||||
| 		if _, ok := gw.ChannelOptions[br.Account+channel]; ok { | ||||
| 			br.ChannelsOut[channel] = gw.ChannelOptions[br.Account+channel] | ||||
| 		} else { | ||||
| 			br.ChannelsOut[channel] = config.ChannelOptions{} | ||||
| 		} | ||||
| func (gw *Gateway) AddConfig(cfg *config.Gateway) error { | ||||
| 	if gw.Names[cfg.Name] { | ||||
| 		return fmt.Errorf("Gateway with name %s already exists", cfg.Name) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) Start() error { | ||||
| 	if cfg.Name == "" { | ||||
| 		return fmt.Errorf("%s", "Gateway without name found") | ||||
| 	} | ||||
| 	log.Infof("Starting gateway: %s", cfg.Name) | ||||
| 	gw.Names[cfg.Name] = true | ||||
| 	gw.Name = cfg.Name | ||||
| 	gw.MyConfig = cfg | ||||
| 	gw.mapChannels() | ||||
| 	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) { | ||||
| 		err := gw.AddBridge(&br) | ||||
| @@ -73,6 +76,18 @@ func (gw *Gateway) Start() error { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) { | ||||
| 	for ID, channel := range gw.Channels { | ||||
| 		if br.Account == channel.Account { | ||||
| 			br.Channels[ID] = *channel | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) Start() error { | ||||
| 	go gw.handleReceive() | ||||
| 	return nil | ||||
| } | ||||
| @@ -88,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 { | ||||
| @@ -109,45 +133,52 @@ RECONNECT: | ||||
| 		time.Sleep(time.Second * 60) | ||||
| 		goto RECONNECT | ||||
| 	} | ||||
| 	br.Joined = make(map[string]bool) | ||||
| 	br.JoinChannels() | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) mapChannels() error { | ||||
| 	options := make(map[string]config.ChannelOptions) | ||||
| 	m := make(map[string][]string) | ||||
| 	for _, br := range gw.MyConfig.Out { | ||||
| 		m[br.Account] = append(m[br.Account], br.Channel) | ||||
| 		options[br.Account+br.Channel] = br.Options | ||||
| 	for _, br := range append(gw.MyConfig.Out, gw.MyConfig.InOut...) { | ||||
| 		ID := br.Channel + br.Account | ||||
| 		_, ok := gw.Channels[ID] | ||||
| 		if !ok { | ||||
| 			channel := &config.ChannelInfo{Name: br.Channel, Direction: "out", ID: ID, Options: br.Options, Account: br.Account, | ||||
| 				GID: make(map[string]bool), SameChannel: make(map[string]bool)} | ||||
| 			channel.GID[gw.Name] = true | ||||
| 			channel.SameChannel[gw.Name] = br.SameChannel | ||||
| 			gw.Channels[channel.ID] = channel | ||||
| 		} | ||||
| 		gw.Channels[ID].GID[gw.Name] = true | ||||
| 		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel | ||||
| 	} | ||||
| 	gw.ChannelsOut = m | ||||
| 	m = nil | ||||
| 	m = make(map[string][]string) | ||||
| 	for _, br := range gw.MyConfig.In { | ||||
| 		m[br.Account] = append(m[br.Account], br.Channel) | ||||
| 		options[br.Account+br.Channel] = br.Options | ||||
|  | ||||
| 	for _, br := range append(gw.MyConfig.In, gw.MyConfig.InOut...) { | ||||
| 		ID := br.Channel + br.Account | ||||
| 		_, ok := gw.Channels[ID] | ||||
| 		if !ok { | ||||
| 			channel := &config.ChannelInfo{Name: br.Channel, Direction: "in", ID: ID, Options: br.Options, Account: br.Account, | ||||
| 				GID: make(map[string]bool), SameChannel: make(map[string]bool)} | ||||
| 			channel.GID[gw.Name] = true | ||||
| 			channel.SameChannel[gw.Name] = br.SameChannel | ||||
| 			gw.Channels[channel.ID] = channel | ||||
| 		} | ||||
| 		gw.Channels[ID].GID[gw.Name] = true | ||||
| 		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel | ||||
| 	} | ||||
| 	gw.ChannelsIn = m | ||||
| 	for _, br := range gw.MyConfig.InOut { | ||||
| 		gw.ChannelsIn[br.Account] = append(gw.ChannelsIn[br.Account], br.Channel) | ||||
| 		gw.ChannelsOut[br.Account] = append(gw.ChannelsOut[br.Account], br.Channel) | ||||
| 		options[br.Account+br.Channel] = br.Options | ||||
| 	} | ||||
| 	gw.ChannelOptions = options | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) getDestChannel(msg *config.Message, dest string) []string { | ||||
| 	channels := gw.ChannelsIn[msg.Account] | ||||
| 	// broadcast to every out channel (irc QUIT) | ||||
| 	if msg.Event == config.EVENT_JOIN_LEAVE && msg.Channel == "" { | ||||
| 		return gw.ChannelsOut[dest] | ||||
| 	} | ||||
| 	for _, channel := range channels { | ||||
| 		if channel == msg.Channel { | ||||
| 			return gw.ChannelsOut[dest] | ||||
| func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo { | ||||
| 	var channels []config.ChannelInfo | ||||
| 	for _, channel := range gw.Channels { | ||||
| 		if _, ok := gw.Channels[getChannelID(*msg)]; !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		if channel.Direction == "out" && channel.Account == dest.Account && gw.validGatewayDest(*msg, channel) { | ||||
| 			channels = append(channels, *channel) | ||||
| 		} | ||||
| 	} | ||||
| 	return []string{} | ||||
| 	return channels | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) { | ||||
| @@ -155,19 +186,20 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) { | ||||
| 	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") | ||||
| 		return | ||||
| 	} | ||||
| 	originchannel := msg.Channel | ||||
| 	channels := gw.DestChannelFunc(&msg, dest.Account) | ||||
| 	for _, channel := range channels { | ||||
| 		// do not send the message to the bridge we come from if also the channel is the same | ||||
| 		if msg.Account == dest.Account && channel == originchannel { | ||||
| 	for _, channel := range gw.DestChannelFunc(&msg, *dest) { | ||||
| 		// do not send to ourself | ||||
| 		if channel.ID == getChannelID(msg) { | ||||
| 			continue | ||||
| 		} | ||||
| 		msg.Channel = channel | ||||
| 		if msg.Channel == "" { | ||||
| 			log.Debug("empty channel") | ||||
| 			return | ||||
| 		} | ||||
| 		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel) | ||||
| 		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" { | ||||
| @@ -194,21 +226,6 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool { | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) modifyMessage(msg *config.Message, dest *bridge.Bridge) { | ||||
| 	val := reflect.ValueOf(gw.Config).Elem() | ||||
| 	for i := 0; i < val.NumField(); i++ { | ||||
| 		typeField := val.Type().Field(i) | ||||
| 		// look for the protocol map (both lowercase) | ||||
| 		if strings.ToLower(typeField.Name) == dest.Protocol { | ||||
| 			// get the Protocol struct from the map | ||||
| 			protoCfg := val.Field(i).MapIndex(reflect.ValueOf(dest.Name)) | ||||
| 			//config.SetNickFormat(msg, protoCfg.Interface().(config.Protocol)) | ||||
| 			val.Field(i).SetMapIndex(reflect.ValueOf(dest.Name), protoCfg) | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) { | ||||
| 	br := gw.Bridges[msg.Account] | ||||
| 	msg.Protocol = br.Protocol | ||||
| @@ -221,3 +238,40 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) { | ||||
| 	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1) | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) validGatewayDest(msg config.Message, channel *config.ChannelInfo) bool { | ||||
| 	GIDmap := gw.Channels[getChannelID(msg)].GID | ||||
| 	// check if we are running a samechannelgateway. | ||||
| 	// if it is and the channel name matches it's ok, otherwise we shouldn't use this channel. | ||||
| 	for k, _ := range GIDmap { | ||||
| 		if channel.SameChannel[k] == true { | ||||
| 			if msg.Channel == channel.Name { | ||||
| 				return true | ||||
| 			} else { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// check if we are in the correct gateway | ||||
| 	for k, _ := range GIDmap { | ||||
| 		if channel.GID[k] == true { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|   | ||||
| @@ -2,48 +2,27 @@ package samechannelgateway | ||||
|  | ||||
| import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/gateway" | ||||
| ) | ||||
|  | ||||
| type SameChannelGateway struct { | ||||
| 	*config.Config | ||||
| 	MyConfig *config.SameChannelGateway | ||||
| 	Channels []string | ||||
| 	Name     string | ||||
| } | ||||
|  | ||||
| func New(cfg *config.Config, gatewayCfg *config.SameChannelGateway) *SameChannelGateway { | ||||
| 	return &SameChannelGateway{ | ||||
| 		MyConfig: gatewayCfg, | ||||
| 		Channels: gatewayCfg.Channels, | ||||
| 		Name:     gatewayCfg.Name, | ||||
| 		Config:   cfg} | ||||
| func New(cfg *config.Config) *SameChannelGateway { | ||||
| 	return &SameChannelGateway{Config: cfg} | ||||
| } | ||||
|  | ||||
| func (sgw *SameChannelGateway) Start() error { | ||||
| 	gw := gateway.New(sgw.Config, &config.Gateway{Name: sgw.Name}) | ||||
| 	gw.DestChannelFunc = sgw.getDestChannel | ||||
| 	for _, account := range sgw.MyConfig.Accounts { | ||||
| 		for _, channel := range sgw.Channels { | ||||
| 			br := config.Bridge{Account: account, Channel: channel} | ||||
| 			gw.MyConfig.InOut = append(gw.MyConfig.InOut, br) | ||||
| func (sgw *SameChannelGateway) GetConfig() []config.Gateway { | ||||
| 	var gwconfigs []config.Gateway | ||||
| 	cfg := sgw.Config | ||||
| 	for _, gw := range cfg.SameChannelGateway { | ||||
| 		gwconfig := config.Gateway{Name: gw.Name, Enable: gw.Enable} | ||||
| 		for _, account := range gw.Accounts { | ||||
| 			for _, channel := range gw.Channels { | ||||
| 				gwconfig.InOut = append(gwconfig.InOut, config.Bridge{Account: account, Channel: channel, SameChannel: true}) | ||||
| 			} | ||||
| 		} | ||||
| 		gwconfigs = append(gwconfigs, gwconfig) | ||||
| 	} | ||||
| 	return gw.Start() | ||||
| } | ||||
|  | ||||
| func (sgw *SameChannelGateway) validChannel(channel string) bool { | ||||
| 	for _, c := range sgw.Channels { | ||||
| 		if c == channel { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (sgw *SameChannelGateway) getDestChannel(msg *config.Message, dest string) []string { | ||||
| 	if sgw.validChannel(msg.Channel) { | ||||
| 		return []string{msg.Channel} | ||||
| 	} | ||||
| 	return []string{} | ||||
| 	return gwconfigs | ||||
| } | ||||
|   | ||||
| @@ -8,10 +8,11 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/gateway/samechannel" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"github.com/google/gops/agent" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	version = "0.10.2" | ||||
| 	version = "0.11.0-dev" | ||||
| 	githash string | ||||
| ) | ||||
|  | ||||
| @@ -39,31 +40,26 @@ func main() { | ||||
| 		log.SetLevel(log.DebugLevel) | ||||
| 	} | ||||
| 	log.Printf("Running version %s %s", version, githash) | ||||
| 	cfg := config.NewConfig(*flagConfig) | ||||
| 	for _, gw := range cfg.SameChannelGateway { | ||||
| 		if !gw.Enable { | ||||
| 			continue | ||||
| 		} | ||||
| 		log.Printf("Starting samechannel gateway %#v", gw.Name) | ||||
| 		g := samechannelgateway.New(cfg, &gw) | ||||
| 		err := g.Start() | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("Starting gateway failed %#v", err) | ||||
| 		} | ||||
| 		log.Printf("Started samechannel gateway %#v", gw.Name) | ||||
| 	if strings.Contains(version, "-dev") { | ||||
| 		log.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.") | ||||
| 	} | ||||
| 	cfg := config.NewConfig(*flagConfig) | ||||
|  | ||||
| 	for _, gw := range cfg.Gateway { | ||||
| 	g := gateway.New(cfg) | ||||
| 	sgw := samechannelgateway.New(cfg) | ||||
| 	gwconfigs := sgw.GetConfig() | ||||
| 	for _, gw := range append(gwconfigs, cfg.Gateway...) { | ||||
| 		if !gw.Enable { | ||||
| 			continue | ||||
| 		} | ||||
| 		log.Printf("Starting gateway %#v", gw.Name) | ||||
| 		g := gateway.New(cfg, &gw) | ||||
| 		err := g.Start() | ||||
| 		err := g.AddConfig(&gw) | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("Starting gateway failed %#v", err) | ||||
| 			log.Fatalf("Starting gateway failed: %s", err) | ||||
| 		} | ||||
| 		log.Printf("Started gateway %#v", gw.Name) | ||||
| 	} | ||||
| 	err := g.Start() | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("Starting gateway failed: %s", err) | ||||
| 	} | ||||
| 	log.Printf("Gateway(s) started succesfully. Now relaying messages") | ||||
| 	select {} | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
| @@ -587,7 +597,7 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> " | ||||
| # | ||||
|  | ||||
| [[gateway]] | ||||
| #OPTIONAL (not used for now) | ||||
| #REQUIRED and UNIQUE | ||||
| name="gateway1" | ||||
| #Enable enables this gateway | ||||
| ##OPTIONAL (default false) | ||||
| @@ -659,6 +669,7 @@ enable=true | ||||
| #channel testing on slack and vice versa. (and for the channel testing2 and testing3) | ||||
|  | ||||
| [[samechannelgateway]] | ||||
|    name="samechannel1" | ||||
|    enable = false | ||||
|    accounts = [ "mattermost.work","slack.hobby" ] | ||||
|    channels = [ "testing","testing2","testing3"] | ||||
|   | ||||
| @@ -34,6 +34,7 @@ type Message struct { | ||||
| 	Channel  string | ||||
| 	Username string | ||||
| 	Text     string | ||||
| 	Type     string | ||||
| } | ||||
|  | ||||
| type Team struct { | ||||
| @@ -177,6 +178,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 +268,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" { | ||||
| @@ -628,6 +631,7 @@ func (m *MMClient) StatusLoop() { | ||||
| 				m.Logout() | ||||
| 				m.WsQuit = false | ||||
| 				m.Login() | ||||
| 				go m.WsReceiver() | ||||
| 			} | ||||
| 		} | ||||
| 		time.Sleep(time.Second * 60) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user