forked from lug/matterbridge
		
	Compare commits
	
		
			46 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | f065e9e4d5 | ||
|   | 3812693111 | ||
|   | dd3c572256 | ||
|   | c5dfe40326 | ||
|   | ef278301e3 | ||
|   | 2888fd64b0 | ||
|   | 27c0f37e49 | ||
|   | 0774f6a5e7 | ||
|   | 4036d4459b | ||
|   | ee643de5b6 | ||
|   | 8c7549a09e | ||
|   | 7a16146304 | ||
|   | 3d3809a21b | ||
|   | 29465397dd | ||
|   | d300bb1735 | ||
|   | 2e703472f1 | ||
|   | 8fede90b9e | ||
|   | d128f157c4 | ||
|   | 4fcedabfd0 | ||
|   | 246c8e4f74 | ||
|   | 4d2207aba7 | ||
|   | 17b8b86d68 | ||
|   | fdb57230a3 | ||
|   | 7469732bbc | ||
|   | d1dd6c3440 | ||
|   | 02612c0061 | ||
|   | a4db63a773 | ||
|   | 035c2b906a | ||
|   | 6ea8be5749 | ||
|   | 36024d5439 | ||
|   | 8d52c98373 | ||
|   | b4a4eb0057 | ||
|   | b469c8ddbd | ||
|   | eee0036c7f | ||
|   | 89c66b9430 | ||
|   | bd38319d83 | ||
|   | 33dffd5ea8 | ||
|   | 57176dadd4 | ||
|   | dd449a8705 | ||
|   | 587ad9f41d | ||
|   | a16ad8bf3b | ||
|   | 1e0490bd36 | ||
|   | 8afc641f0c | ||
|   | 2e4d58cb92 | ||
|   | 02d7e2db65 | ||
|   | f935c573e9 | 
| @@ -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.9.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.2](https://github.com/42wim/matterbridge/releases/latest) | ||||
| * Latest stable release [v0.12.1](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) | ||||
| 			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,9 +26,21 @@ 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 | ||||
| 	EditSuffix             string // mattermost, slack, discord, telegram, gitter | ||||
| 	EditDisable            bool   // mattermost, slack, discord, telegram, gitter | ||||
| 	IconURL                string // mattermost, slack | ||||
| 	IgnoreNicks            string // all protocols | ||||
| 	Jid                    string // xmpp | ||||
| @@ -39,6 +52,7 @@ type Protocol struct { | ||||
| 	NickServNick           string // IRC | ||||
| 	NickServPassword       string // IRC | ||||
| 	NicksPerRow            int    // mattermost, slack | ||||
| 	NoHomeServerSuffix     bool   // matrix | ||||
| 	NoTLS                  bool   // mattermost | ||||
| 	Password               string // IRC,mattermost,XMPP,matrix | ||||
| 	PrefixMessagesWithNick bool   // mattemost, slack | ||||
| @@ -56,6 +70,7 @@ type Protocol struct { | ||||
| 	UseAPI                 bool   // mattermost, slack | ||||
| 	UseSASL                bool   // IRC | ||||
| 	UseTLS                 bool   // IRC | ||||
| 	UseFirstName           bool   // telegram | ||||
| } | ||||
|  | ||||
| type ChannelOptions struct { | ||||
| @@ -63,9 +78,10 @@ type ChannelOptions struct { | ||||
| } | ||||
|  | ||||
| type Bridge struct { | ||||
| 	Account string | ||||
| 	Channel string | ||||
| 	Options ChannelOptions | ||||
| 	Account     string | ||||
| 	Channel     string | ||||
| 	Options     ChannelOptions | ||||
| 	SameChannel bool | ||||
| } | ||||
|  | ||||
| type Gateway struct { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"github.com/bwmarrin/discordgo" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| ) | ||||
| @@ -51,6 +52,7 @@ func (b *bdiscord) Connect() error { | ||||
| 	flog.Info("Connection succeeded") | ||||
| 	b.c.AddHandler(b.messageCreate) | ||||
| 	b.c.AddHandler(b.memberUpdate) | ||||
| 	b.c.AddHandler(b.messageUpdate) | ||||
| 	err = b.c.Open() | ||||
| 	if err != nil { | ||||
| 		flog.Debugf("%#v", err) | ||||
| @@ -103,6 +105,18 @@ func (b *bdiscord) Send(msg config.Message) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) { | ||||
| 	if b.Config.EditDisable { | ||||
| 		return | ||||
| 	} | ||||
| 	// only when message is actually edited | ||||
| 	if m.Message.EditedTimestamp != "" { | ||||
| 		flog.Debugf("Sending edit message") | ||||
| 		m.Content = m.Content + b.Config.EditSuffix | ||||
| 		b.messageCreate(s, (*discordgo.MessageCreate)(m)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { | ||||
| 	// not relay our own messages | ||||
| 	if m.Author.Username == b.Nick { | ||||
| @@ -125,6 +139,8 @@ func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | ||||
| 	if len(m.MentionRoles) > 0 { | ||||
| 		m.Message.Content = b.replaceRoleMentions(m.Message.Content) | ||||
| 	} | ||||
| 	m.Message.Content = b.stripCustomoji(m.Message.Content) | ||||
| 	m.Message.Content = b.replaceChannelMentions(m.Message.Content) | ||||
| 	b.Remote <- config.Message{Username: username, Text: m.ContentWithMentionsReplaced(), Channel: channelName, | ||||
| 		Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg"} | ||||
| } | ||||
| @@ -143,12 +159,14 @@ func (b *bdiscord) getNick(user *discordgo.User) string { | ||||
| 	b.Lock() | ||||
| 	defer b.Unlock() | ||||
| 	if _, ok := b.userMemberMap[user.ID]; ok { | ||||
| 		if b.userMemberMap[user.ID].Nick != "" { | ||||
| 			// only return if nick is set | ||||
| 			return b.userMemberMap[user.ID].Nick | ||||
| 		if b.userMemberMap[user.ID] != nil { | ||||
| 			if b.userMemberMap[user.ID].Nick != "" { | ||||
| 				// only return if nick is set | ||||
| 				return b.userMemberMap[user.ID].Nick | ||||
| 			} | ||||
| 			// otherwise return username | ||||
| 			return user.Username | ||||
| 		} | ||||
| 		// otherwise return username | ||||
| 		return user.Username | ||||
| 	} | ||||
| 	// if we didn't find nick, search for it | ||||
| 	b.userMemberMap[user.ID], err = b.c.GuildMember(b.guildID, user.ID) | ||||
| @@ -195,3 +213,28 @@ func (b *bdiscord) replaceRoleMentions(text string) string { | ||||
| 	} | ||||
| 	return text | ||||
| } | ||||
|  | ||||
| func (b *bdiscord) replaceChannelMentions(text string) string { | ||||
| 	var err error | ||||
| 	re := regexp.MustCompile("<#[0-9]+>") | ||||
| 	text = re.ReplaceAllStringFunc(text, func(m string) string { | ||||
| 		channel := b.getChannelName(m[2 : len(m)-1]) | ||||
| 		// if at first don't succeed, try again | ||||
| 		if channel == "" { | ||||
| 			b.Channels, err = b.c.GuildChannels(b.guildID) | ||||
| 			if err != nil { | ||||
| 				return "#unknownchannel" | ||||
| 			} | ||||
| 			channel = b.getChannelName(m[2 : len(m)-1]) | ||||
| 			return channel | ||||
| 		} | ||||
| 		return channel | ||||
| 	}) | ||||
| 	return text | ||||
| } | ||||
|  | ||||
| func (b *bdiscord) stripCustomoji(text string) string { | ||||
| 	// <:doge:302803592035958784> | ||||
| 	re := regexp.MustCompile("<(:.*?:)[0-9]+>") | ||||
| 	return re.ReplaceAllString(text, `$1`) | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
| @@ -109,7 +109,6 @@ func (b *Birc) Send(msg config.Message) error { | ||||
| 	} | ||||
| 	if strings.HasPrefix(msg.Text, "!") { | ||||
| 		b.Command(&msg) | ||||
| 		return nil | ||||
| 	} | ||||
| 	for _, text := range strings.Split(msg.Text, "\n") { | ||||
| 		if len(b.Local) < b.Config.MessageQueue { | ||||
| @@ -167,14 +166,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 +186,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) | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,12 @@ | ||||
| package bmatrix | ||||
|  | ||||
| import ( | ||||
| 	"regexp" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	matrix "github.com/matrix-org/gomatrix" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| type Bmatrix struct { | ||||
| @@ -101,8 +103,13 @@ func (b *Bmatrix) handlematrix() error { | ||||
| 				flog.Debugf("Unknown room %s", ev.RoomID) | ||||
| 				return | ||||
| 			} | ||||
| 			username := ev.Sender[1:] | ||||
| 			if b.Config.NoHomeServerSuffix { | ||||
| 				re := regexp.MustCompile("(.*?):.*") | ||||
| 				username = re.ReplaceAllString(username, `$1`) | ||||
| 			} | ||||
| 			flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account) | ||||
| 			b.Remote <- config.Message{Username: ev.Sender, Text: ev.Content["body"].(string), Channel: channel, Account: b.Account} | ||||
| 			b.Remote <- config.Message{Username: username, Text: ev.Content["body"].(string), Channel: channel, Account: b.Account} | ||||
| 		} | ||||
| 		flog.Debugf("Received: %#v", ev) | ||||
| 	}) | ||||
|   | ||||
| @@ -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,14 +133,26 @@ 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 { | ||||
| 		if (message.Raw.Event == "posted" || message.Raw.Event == "post_edited") && | ||||
| 			b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId { | ||||
| 			flog.Debugf("Receiving from matterclient %#v", message) | ||||
| 			m := &MMMessage{} | ||||
| 			m.Username = message.Username | ||||
| 			m.Channel = message.Channel | ||||
| 			m.Text = message.Text | ||||
| 			if message.Raw.Event == "post_edited" && !b.Config.EditDisable { | ||||
| 				m.Text = message.Text + b.Config.EditSuffix | ||||
| 			} | ||||
| 			if len(message.Post.FileIds) > 0 { | ||||
| 				for _, link := range b.mc.GetPublicLinks(message.Post.FileIds) { | ||||
| 					m.Text = m.Text + "\n" + link | ||||
|   | ||||
| @@ -73,9 +73,15 @@ 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 | ||||
| 			if err.Error() != "name_taken" { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| @@ -195,6 +201,11 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) { | ||||
| 			// ignore first message | ||||
| 			if count > 0 { | ||||
| 				flog.Debugf("Receiving from slackclient %#v", ev) | ||||
| 				if !b.Config.EditDisable && ev.SubMessage != nil { | ||||
| 					flog.Debugf("SubMessage %#v", ev.SubMessage) | ||||
| 					ev.User = ev.SubMessage.User | ||||
| 					ev.Text = ev.SubMessage.Text + b.Config.EditSuffix | ||||
| 				} | ||||
| 				// use our own func because rtm.GetChannelInfo doesn't work for private channels | ||||
| 				channel, err := b.getChannelByID(ev.Channel) | ||||
| 				if err != nil { | ||||
|   | ||||
| @@ -80,28 +80,37 @@ 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 && !b.Config.EditDisable { | ||||
| 			message = update.EditedChannelPost | ||||
| 			message.Text = message.Text + b.Config.EditSuffix | ||||
| 		} | ||||
| 		// handle groups | ||||
| 		if update.Message != nil { | ||||
| 			if update.Message.From != nil { | ||||
| 				username = update.Message.From.FirstName | ||||
| 			message = update.Message | ||||
| 		} | ||||
| 		if update.EditedMessage != nil && !b.Config.EditDisable { | ||||
| 			message = update.EditedMessage | ||||
| 			message.Text = message.Text + b.Config.EditSuffix | ||||
| 		} | ||||
| 		if message.From != nil { | ||||
| 			if b.Config.UseFirstName { | ||||
| 				username = message.From.FirstName | ||||
| 			} | ||||
| 			if username == "" { | ||||
| 				username = message.From.UserName | ||||
| 				if username == "" { | ||||
| 					username = update.Message.From.UserName | ||||
| 					username = message.From.FirstName | ||||
| 				} | ||||
| 			} | ||||
| 			text = update.Message.Text | ||||
| 			channel = strconv.FormatInt(update.Message.Chat.ID, 10) | ||||
| 			text = message.Text | ||||
| 			channel = strconv.FormatInt(message.Chat.ID, 10) | ||||
| 		} | ||||
|  | ||||
| 		if username == "" { | ||||
| 			username = "unknown" | ||||
| 		} | ||||
|   | ||||
| @@ -119,7 +119,7 @@ func (b *Bxmpp) handleXmpp() error { | ||||
| 			var channel, nick string | ||||
| 			if v.Type == "groupchat" { | ||||
| 				s := strings.Split(v.Remote, "@") | ||||
| 				if len(s) == 2 { | ||||
| 				if len(s) >= 2 { | ||||
| 					channel = s[0] | ||||
| 				} | ||||
| 				s = strings.Split(s[1], "/") | ||||
|   | ||||
							
								
								
									
										48
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,3 +1,51 @@ | ||||
| # v0.12.1 | ||||
| ## New features | ||||
| * telegram: Add UseFirstName option (telegram). Closes #144 | ||||
| * matrix: Add NoHomeServerSuffix. Option to disable homeserver on username (matrix). Closes #160. | ||||
|  | ||||
| ## Bugfix | ||||
| * xmpp: Add Compatibility for Cisco Jabber (xmpp) (#166) | ||||
| * irc: Fix JoinChannel argument to use IRC channel key (#172) | ||||
| * discord: Fix possible crash on nil (discord) | ||||
| * discord: Replace long ids in channel metions (discord). Fixes #174 | ||||
|  | ||||
| # v0.12.0 | ||||
| ## Changes | ||||
| * general: edited messages are now being sent by default on discord/mattermost/telegram/slack. See "New Features" | ||||
|  | ||||
| ## New features | ||||
| * general: add support for edited messages.  | ||||
|   Add new keyword EditDisable (false/true), default false. Which means by default edited messages will be sent to other bridges. | ||||
|   Add new keyword EditSuffix , default "". You can change this eg to "(edited)", this will be appended to every edit message. | ||||
| * mattermost: support mattermost v3.9.x | ||||
| * general: Add support for HTTP{S}_PROXY env variables (#162) | ||||
| * discord: Strip custom emoji metadata (discord). Closes #148 | ||||
|  | ||||
| ## Bugfix | ||||
| * slack: Ignore error on private channel join (slack) Fixes #150  | ||||
| * mattermost: fix crash on reconnects when server is down. Closes #163 | ||||
| * irc: Relay messages starting with ! (irc). Closes #164 | ||||
|  | ||||
| # v0.11.0 | ||||
| ## 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) | ||||
| * mattermost: Support mattermost v3.8.x | ||||
| * 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.12.1" | ||||
| 	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 | ||||
|  | ||||
| @@ -197,9 +200,9 @@ IconURL="http://youricon.png" | ||||
| #OPTIONAL | ||||
| useAPI=false | ||||
|  | ||||
| #The mattermost hostname.  | ||||
| #The mattermost hostname. (do not prefix it with http or https) | ||||
| #REQUIRED (when useAPI=true) | ||||
| Server="yourmattermostserver.domain" | ||||
| Server="yourmattermostserver.domain"  | ||||
|  | ||||
| #Your team on mattermost.  | ||||
| #REQUIRED (when useAPI=true) | ||||
| @@ -238,6 +241,14 @@ NicksPerRow=4 | ||||
| #OPTIONAL (default false) | ||||
| PrefixMessagesWithNick=false | ||||
|  | ||||
| #Disable sending of edits to other bridges | ||||
| #OPTIONAL (default false) | ||||
| EditDisable=false | ||||
|  | ||||
| #Message to be appended to every edited message | ||||
| #OPTIONAL (default empty) | ||||
| EditSuffix=" (edited)" | ||||
|  | ||||
| #Nicks you want to ignore.  | ||||
| #Messages from those users will not be sent to other bridges. | ||||
| #OPTIONAL | ||||
| @@ -250,7 +261,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 +294,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 | ||||
|  | ||||
| @@ -342,6 +355,14 @@ NickFormatter="plain" | ||||
| #OPTIONAL (default 4) | ||||
| NicksPerRow=4 | ||||
|  | ||||
| #Disable sending of edits to other bridges | ||||
| #OPTIONAL (default false) | ||||
| EditDisable=true | ||||
|  | ||||
| #Message to be appended to every edited message | ||||
| #OPTIONAL (default empty) | ||||
| EditSuffix=" (edited)" | ||||
|  | ||||
| #Whether to prefix messages from other bridges to mattermost with RemoteNickFormat | ||||
| #Useful if username overrides for incoming webhooks isn't enabled on the  | ||||
| #slack server. If you set PrefixMessagesWithNick to true, each message  | ||||
| @@ -362,7 +383,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 | ||||
|  | ||||
| @@ -385,6 +407,14 @@ Token="Yourtokenhere" | ||||
| #REQUIRED | ||||
| Server="yourservername" | ||||
|  | ||||
| #Disable sending of edits to other bridges | ||||
| #OPTIONAL (default false) | ||||
| EditDisable=false | ||||
|  | ||||
| #Message to be appended to every edited message | ||||
| #OPTIONAL (default empty) | ||||
| EditSuffix=" (edited)" | ||||
|  | ||||
| #Nicks you want to ignore.  | ||||
| #Messages from those users will not be sent to other bridges. | ||||
| #OPTIONAL | ||||
| @@ -397,7 +427,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 | ||||
|  | ||||
| @@ -420,6 +451,20 @@ Token="Yourtokenhere" | ||||
| #See https://core.telegram.org/bots/api#html-style | ||||
| MessageFormat="" | ||||
|  | ||||
| #If enabled use the "First Name" as username. If this is empty use the Username | ||||
| #If disabled use the "Username" as username. If this is empty use the First Name  | ||||
| #If all names are empty, username will be "unknown" | ||||
| #OPTIONAL (default false) | ||||
| UseFirstName=false | ||||
|  | ||||
| #Disable sending of edits to other bridges | ||||
| #OPTIONAL (default false) | ||||
| EditDisable=false | ||||
|  | ||||
| #Message to be appended to every edited message | ||||
| #OPTIONAL (default empty) | ||||
| EditSuffix=" (edited)" | ||||
|  | ||||
| #Nicks you want to ignore.  | ||||
| #Messages from those users will not be sent to other bridges. | ||||
| #OPTIONAL | ||||
| @@ -432,7 +477,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 +535,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 | ||||
|  | ||||
| @@ -513,6 +560,11 @@ Server="https://matrix.org" | ||||
| Login="yourlogin" | ||||
| Password="yourpass" | ||||
|  | ||||
| #Whether to send the homeserver suffix. eg ":matrix.org" in @username:matrix.org | ||||
| #to other bridges, or only send "username".(true only sends username) | ||||
| #OPTIONAL (default false) | ||||
| NoHomeServerSuffix=false | ||||
|  | ||||
| #Whether to prefix messages from other bridges to matrix with the sender's nick.  | ||||
| #Useful if username overrides for incoming webhooks isn't enabled on the  | ||||
| #matrix server. If you set PrefixMessagesWithNick to true, each message  | ||||
| @@ -532,7 +584,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 +640,7 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> " | ||||
| # | ||||
|  | ||||
| [[gateway]] | ||||
| #OPTIONAL (not used for now) | ||||
| #REQUIRED and UNIQUE | ||||
| name="gateway1" | ||||
| #Enable enables this gateway | ||||
| ##OPTIONAL (default false) | ||||
| @@ -615,7 +668,8 @@ enable=true | ||||
|     #             see (https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau) | ||||
|     #hipchat    - id_channel (see https://www.hipchat.com/account/xmpp for the correct channel) | ||||
|     #rocketchat - #channel (# is required) | ||||
|     #matrix     - #channel:server (eg #yourchannel:matrix.org) | ||||
|     #matrix     - #channel:server (eg #yourchannel:matrix.org)  | ||||
|     #           - encrypted rooms are not supported in matrix | ||||
|     #REQUIRED | ||||
|     channel="#testing" | ||||
|  | ||||
| @@ -659,6 +713,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"] | ||||
|   | ||||
| @@ -6,7 +6,8 @@ | ||||
| [mattermost] | ||||
|     [mattermost.work] | ||||
|     useAPI=true | ||||
|     Server="yourmattermostserver.domain" | ||||
|     #do not prefix it wit http:// or https:// | ||||
|     Server="yourmattermostserver.domain"  | ||||
|     Team="yourteam" | ||||
|     Login="yourlogin" | ||||
|     Password="yourpass" | ||||
|   | ||||
| @@ -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 { | ||||
| @@ -102,8 +106,27 @@ func (m *MMClient) Login() error { | ||||
| 	} | ||||
| 	// login to mattermost | ||||
| 	m.Client = model.NewClient(uriScheme + m.Credentials.Server) | ||||
| 	m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}} | ||||
| 	m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}, Proxy: http.ProxyFromEnvironment} | ||||
| 	m.Client.HttpClient.Timeout = time.Second * 10 | ||||
|  | ||||
| 	for { | ||||
| 		d := b.Duration() | ||||
| 		// 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 | ||||
| 		if m.ServerVersion == "" { | ||||
| 			m.log.Debugf("Server not up yet, reconnecting in %s", d) | ||||
| 			time.Sleep(d) | ||||
| 		} else { | ||||
| 			m.log.Infof("Found version %s", m.ServerVersion) | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	b.Reset() | ||||
|  | ||||
| 	var myinfo *model.Result | ||||
| 	var appErr *model.AppError | ||||
| 	var logmsg = "trying login" | ||||
| @@ -177,6 +200,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 | ||||
| @@ -238,7 +262,7 @@ func (m *MMClient) WsReceiver() { | ||||
|  | ||||
| func (m *MMClient) parseMessage(rmsg *Message) { | ||||
| 	switch rmsg.Raw.Event { | ||||
| 	case model.WEBSOCKET_EVENT_POSTED: | ||||
| 	case model.WEBSOCKET_EVENT_POSTED, model.WEBSOCKET_EVENT_POST_EDITED: | ||||
| 		m.parseActionPost(rmsg) | ||||
| 		/* | ||||
| 			case model.ACTION_USER_REMOVED: | ||||
| @@ -266,7 +290,18 @@ func (m *MMClient) parseActionPost(rmsg *Message) { | ||||
| 	} | ||||
| 	rmsg.Username = m.GetUser(data.UserId).Username | ||||
| 	rmsg.Channel = m.GetChannelName(data.ChannelId) | ||||
| 	rmsg.Team = m.GetTeamName(rmsg.Raw.Data["team_id"].(string)) | ||||
| 	rmsg.Type = data.Type | ||||
| 	teamid, _ := rmsg.Raw.Data["team_id"].(string) | ||||
| 	// edit messsages have no team_id for some reason | ||||
| 	if teamid == "" { | ||||
| 		// we can find the team_id from the channelid | ||||
| 		result, _ := m.Client.GetChannel(data.ChannelId, "") | ||||
| 		teamid = result.Data.(*model.ChannelData).Channel.TeamId | ||||
| 		rmsg.Raw.Data["team_id"] = teamid | ||||
| 	} | ||||
| 	if teamid != "" { | ||||
| 		rmsg.Team = m.GetTeamName(teamid) | ||||
| 	} | ||||
| 	// direct message | ||||
| 	if rmsg.Raw.Data["channel_type"] == "D" { | ||||
| 		rmsg.Channel = m.GetUser(data.UserId).Username | ||||
| @@ -292,7 +327,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 +467,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 +676,7 @@ func (m *MMClient) StatusLoop() { | ||||
| 				m.Logout() | ||||
| 				m.WsQuit = false | ||||
| 				m.Login() | ||||
| 				go m.WsReceiver() | ||||
| 			} | ||||
| 		} | ||||
| 		time.Sleep(time.Second * 60) | ||||
| @@ -659,7 +708,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 +740,19 @@ 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") || | ||||
| 		strings.HasPrefix(version, "3.9.0") { | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| import l4g "code.google.com/p/log4go" | ||||
|  | ||||
| func main() { | ||||
| 	log := l4g.NewLogger() | ||||
| 	defer log.Close() | ||||
| 	log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter()) | ||||
| 	log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) | ||||
| } | ||||
| @@ -1,57 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| import l4g "code.google.com/p/log4go" | ||||
|  | ||||
| const ( | ||||
| 	filename = "flw.log" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	// Get a new logger instance | ||||
| 	log := l4g.NewLogger() | ||||
|  | ||||
| 	// Create a default logger that is logging messages of FINE or higher | ||||
| 	log.AddFilter("file", l4g.FINE, l4g.NewFileLogWriter(filename, false)) | ||||
| 	log.Close() | ||||
|  | ||||
| 	/* Can also specify manually via the following: (these are the defaults) */ | ||||
| 	flw := l4g.NewFileLogWriter(filename, false) | ||||
| 	flw.SetFormat("[%D %T] [%L] (%S) %M") | ||||
| 	flw.SetRotate(false) | ||||
| 	flw.SetRotateSize(0) | ||||
| 	flw.SetRotateLines(0) | ||||
| 	flw.SetRotateDaily(false) | ||||
| 	log.AddFilter("file", l4g.FINE, flw) | ||||
|  | ||||
| 	// Log some experimental messages | ||||
| 	log.Finest("Everything is created now (notice that I will not be printing to the file)") | ||||
| 	log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) | ||||
| 	log.Critical("Time to close out!") | ||||
|  | ||||
| 	// Close the log | ||||
| 	log.Close() | ||||
|  | ||||
| 	// Print what was logged to the file (yes, I know I'm skipping error checking) | ||||
| 	fd, _ := os.Open(filename) | ||||
| 	in := bufio.NewReader(fd) | ||||
| 	fmt.Print("Messages logged to file were: (line numbers not included)\n") | ||||
| 	for lineno := 1; ; lineno++ { | ||||
| 		line, err := in.ReadString('\n') | ||||
| 		if err == io.EOF { | ||||
| 			break | ||||
| 		} | ||||
| 		fmt.Printf("%3d:\t%s", lineno, line) | ||||
| 	} | ||||
| 	fd.Close() | ||||
|  | ||||
| 	// Remove the file so it's not lying around | ||||
| 	os.Remove(filename) | ||||
| } | ||||
| @@ -1,42 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	port = flag.String("p", "12124", "Port number to listen on") | ||||
| ) | ||||
|  | ||||
| func e(err error) { | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Erroring out: %s\n", err) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
|  | ||||
| 	// Bind to the port | ||||
| 	bind, err := net.ResolveUDPAddr("0.0.0.0:" + *port) | ||||
| 	e(err) | ||||
|  | ||||
| 	// Create listener | ||||
| 	listener, err := net.ListenUDP("udp", bind) | ||||
| 	e(err) | ||||
|  | ||||
| 	fmt.Printf("Listening to port %s...\n", *port) | ||||
| 	for { | ||||
| 		// read into a new buffer | ||||
| 		buffer := make([]byte, 1024) | ||||
| 		_, _, err := listener.ReadFrom(buffer) | ||||
| 		e(err) | ||||
|  | ||||
| 		// log to standard output | ||||
| 		fmt.Println(string(buffer)) | ||||
| 	} | ||||
| } | ||||
| @@ -1,18 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| import l4g "code.google.com/p/log4go" | ||||
|  | ||||
| func main() { | ||||
| 	log := l4g.NewLogger() | ||||
| 	log.AddFilter("network", l4g.FINEST, l4g.NewSocketLogWriter("udp", "192.168.1.255:12124")) | ||||
|  | ||||
| 	// Run `nc -u -l -p 12124` or similar before you run this to see the following message | ||||
| 	log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) | ||||
|  | ||||
| 	// This makes sure the output stream buffer is written | ||||
| 	log.Close() | ||||
| } | ||||
| @@ -1,13 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import l4g "code.google.com/p/log4go" | ||||
|  | ||||
| func main() { | ||||
| 	// Load the configuration (isn't this easy?) | ||||
| 	l4g.LoadConfiguration("example.xml") | ||||
|  | ||||
| 	// And now we're ready! | ||||
| 	l4g.Finest("This will only go to those of you really cool UDP kids!  If you change enabled=true.") | ||||
| 	l4g.Debug("Oh no!  %d + %d = %d!", 2, 2, 2+2) | ||||
| 	l4g.Info("About that time, eh chaps?") | ||||
| } | ||||
		Reference in New Issue
	
	Block a user