forked from lug/matterbridge
		
	Split Discord bridge in multiple files (#632)
This commit is contained in:
		| @@ -2,9 +2,7 @@ package bdiscord | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
|  | ||||
| @@ -17,18 +15,23 @@ import ( | ||||
| const MessageLength = 1950 | ||||
|  | ||||
| type Bdiscord struct { | ||||
| 	c              *discordgo.Session | ||||
| 	Channels       []*discordgo.Channel | ||||
| 	Nick           string | ||||
| 	UseChannelID   bool | ||||
| 	userMemberMap  map[string]*discordgo.Member | ||||
| 	nickMemberMap  map[string]*discordgo.Member | ||||
| 	guildID        string | ||||
| 	webhookID      string | ||||
| 	webhookToken   string | ||||
| 	channelInfoMap map[string]*config.ChannelInfo | ||||
| 	sync.RWMutex | ||||
| 	*bridge.Config | ||||
|  | ||||
| 	c *discordgo.Session | ||||
|  | ||||
| 	nick         string | ||||
| 	useChannelID bool | ||||
| 	guildID      string | ||||
| 	webhookID    string | ||||
| 	webhookToken string | ||||
|  | ||||
| 	channelsMutex  sync.RWMutex | ||||
| 	channels       []*discordgo.Channel | ||||
| 	channelInfoMap map[string]*config.ChannelInfo | ||||
|  | ||||
| 	membersMutex  sync.RWMutex | ||||
| 	userMemberMap map[string]*discordgo.Member | ||||
| 	nickMemberMap map[string]*discordgo.Member | ||||
| } | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| @@ -77,28 +80,40 @@ func (b *Bdiscord) Connect() error { | ||||
| 		return err | ||||
| 	} | ||||
| 	serverName := strings.Replace(b.GetString("Server"), "ID:", "", -1) | ||||
| 	b.Nick = userinfo.Username | ||||
| 	b.nick = userinfo.Username | ||||
| 	b.channelsMutex.Lock() | ||||
| 	for _, guild := range guilds { | ||||
| 		if guild.Name == serverName || guild.ID == serverName { | ||||
| 			b.Channels, err = b.c.GuildChannels(guild.ID) | ||||
| 			b.channels, err = b.c.GuildChannels(guild.ID) | ||||
| 			b.guildID = guild.ID | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	for _, channel := range b.Channels { | ||||
| 	b.channelsMutex.Unlock() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	b.channelsMutex.RLock() | ||||
| 	for _, channel := range b.channels { | ||||
| 		b.Log.Debugf("found channel %#v", channel) | ||||
| 	} | ||||
| 	// obtaining guild members and initializing nickname mapping | ||||
| 	b.Lock() | ||||
| 	defer b.Unlock() | ||||
| 	b.channelsMutex.RUnlock() | ||||
|  | ||||
| 	// Obtaining guild members and initializing nickname mapping. | ||||
| 	b.membersMutex.Lock() | ||||
| 	defer b.membersMutex.Unlock() | ||||
| 	members, err := b.c.GuildMembers(b.guildID, "", 1000) | ||||
| 	if err != nil { | ||||
| 		b.Log.Error("Error obtaining guild members", err) | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, member := range members { | ||||
| 		if member == nil { | ||||
| 			b.Log.Warnf("Skipping missing information for a user.") | ||||
| 			continue | ||||
| 		} | ||||
| 		b.userMemberMap[member.User.ID] = member | ||||
| 		b.nickMemberMap[member.User.Username] = member | ||||
| 		if member.Nick != "" { | ||||
| @@ -113,10 +128,13 @@ func (b *Bdiscord) Disconnect() error { | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error { | ||||
| 	b.channelsMutex.Lock() | ||||
| 	defer b.channelsMutex.Unlock() | ||||
|  | ||||
| 	b.channelInfoMap[channel.ID] = &channel | ||||
| 	idcheck := strings.Split(channel.Name, "ID:") | ||||
| 	if len(idcheck) > 1 { | ||||
| 		b.UseChannelID = true | ||||
| 		b.useChannelID = true | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| @@ -139,11 +157,13 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | ||||
| 	wToken := b.webhookToken | ||||
|  | ||||
| 	// check if have a channel specific webhook | ||||
| 	b.channelsMutex.RLock() | ||||
| 	if ci, ok := b.channelInfoMap[msg.Channel+b.Account]; ok { | ||||
| 		if ci.Options.WebhookURL != "" { | ||||
| 			wID, wToken = b.splitURL(ci.Options.WebhookURL) | ||||
| 		} | ||||
| 	} | ||||
| 	b.channelsMutex.RUnlock() | ||||
|  | ||||
| 	// Use webhook to send the message | ||||
| 	if wID != "" { | ||||
| @@ -196,7 +216,9 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | ||||
| 	if msg.Extra != nil { | ||||
| 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | ||||
| 			rmsg.Text = helper.ClipMessage(rmsg.Text, MessageLength) | ||||
| 			b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text) | ||||
| 			if _, err := b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text); err != nil { | ||||
| 				b.Log.Errorf("Could not send message %#v: %v", rmsg, err) | ||||
| 			} | ||||
| 		} | ||||
| 		// check if we have files to upload (from slack, telegram or mattermost) | ||||
| 		if len(msg.Extra["file"]) > 0 { | ||||
| @@ -221,246 +243,15 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | ||||
| 	return res.ID, err | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) { | ||||
| 	rmsg := config.Message{Account: b.Account, ID: m.ID, Event: config.EventMsgDelete, Text: config.EventMsgDelete} | ||||
| 	rmsg.Channel = b.getChannelName(m.ChannelID) | ||||
| 	if b.UseChannelID { | ||||
| 		rmsg.Channel = "ID:" + m.ChannelID | ||||
| 	} | ||||
| 	b.Log.Debugf("<= Sending message from %s to gateway", b.Account) | ||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||
| 	b.Remote <- rmsg | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) { | ||||
| 	if b.GetBool("EditDisable") { | ||||
| 		return | ||||
| 	} | ||||
| 	// only when message is actually edited | ||||
| 	if m.Message.EditedTimestamp != "" { | ||||
| 		b.Log.Debugf("Sending edit message") | ||||
| 		m.Content += b.GetString("EditSuffix") | ||||
| 		b.messageCreate(s, (*discordgo.MessageCreate)(m)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { | ||||
| 	var err error | ||||
|  | ||||
| 	// not relay our own messages | ||||
| 	if m.Author.Username == b.Nick { | ||||
| 		return | ||||
| 	} | ||||
| 	// if using webhooks, do not relay if it's ours | ||||
| 	if b.useWebhook() && m.Author.Bot && b.isWebhookID(m.Author.ID) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// add the url of the attachments to content | ||||
| 	if len(m.Attachments) > 0 { | ||||
| 		for _, attach := range m.Attachments { | ||||
| 			m.Content = m.Content + "\n" + attach.URL | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	rmsg := config.Message{Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg", UserID: m.Author.ID, ID: m.ID} | ||||
|  | ||||
| 	if m.Content != "" { | ||||
| 		b.Log.Debugf("== Receiving event %#v", m.Message) | ||||
| 		m.Message.Content = b.stripCustomoji(m.Message.Content) | ||||
| 		m.Message.Content = b.replaceChannelMentions(m.Message.Content) | ||||
| 		rmsg.Text, err = m.ContentWithMoreMentionsReplaced(b.c) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("ContentWithMoreMentionsReplaced failed: %s", err) | ||||
| 			rmsg.Text = m.ContentWithMentionsReplaced() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// set channel name | ||||
| 	rmsg.Channel = b.getChannelName(m.ChannelID) | ||||
| 	if b.UseChannelID { | ||||
| 		rmsg.Channel = "ID:" + m.ChannelID | ||||
| 	} | ||||
|  | ||||
| 	// set username | ||||
| 	if !b.GetBool("UseUserName") { | ||||
| 		rmsg.Username = b.getNick(m.Author) | ||||
| 	} else { | ||||
| 		rmsg.Username = m.Author.Username | ||||
| 	} | ||||
|  | ||||
| 	// if we have embedded content add it to text | ||||
| 	if b.GetBool("ShowEmbeds") && m.Message.Embeds != nil { | ||||
| 		for _, embed := range m.Message.Embeds { | ||||
| 			rmsg.Text = rmsg.Text + "embed: " + embed.Title + " - " + embed.Description + " - " + embed.URL + "\n" | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// no empty messages | ||||
| 	if rmsg.Text == "" { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// do we have a /me action | ||||
| 	var ok bool | ||||
| 	rmsg.Text, ok = b.replaceAction(rmsg.Text) | ||||
| 	if ok { | ||||
| 		rmsg.Event = config.EventUserAction | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", m.Author.Username, b.Account) | ||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||
| 	b.Remote <- rmsg | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) { | ||||
| 	b.Lock() | ||||
| 	if _, ok := b.userMemberMap[m.Member.User.ID]; ok { | ||||
| 		b.Log.Debugf("%s: memberupdate: user %s (nick %s) changes nick to %s", b.Account, m.Member.User.Username, b.userMemberMap[m.Member.User.ID].Nick, m.Member.Nick) | ||||
| 	} | ||||
| 	b.userMemberMap[m.Member.User.ID] = m.Member | ||||
| 	b.nickMemberMap[m.Member.User.Username] = m.Member | ||||
| 	if m.Member.Nick != "" { | ||||
| 		b.nickMemberMap[m.Member.Nick] = m.Member | ||||
| 	} | ||||
| 	b.Unlock() | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) getNick(user *discordgo.User) string { | ||||
| 	var err error | ||||
| 	b.Lock() | ||||
| 	defer b.Unlock() | ||||
| 	if _, ok := b.userMemberMap[user.ID]; ok { | ||||
| 		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 | ||||
| 		} | ||||
| 	} | ||||
| 	// if we didn't find nick, search for it | ||||
| 	member, err := b.c.GuildMember(b.guildID, user.ID) | ||||
| 	if err != nil { | ||||
| 		return user.Username | ||||
| 	} | ||||
| 	b.userMemberMap[user.ID] = member | ||||
| 	// only return if nick is set | ||||
| 	if b.userMemberMap[user.ID].Nick != "" { | ||||
| 		return b.userMemberMap[user.ID].Nick | ||||
| 	} | ||||
| 	return user.Username | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) getGuildMemberByNick(nick string) (*discordgo.Member, error) { | ||||
| 	b.Lock() | ||||
| 	defer b.Unlock() | ||||
| 	if _, ok := b.nickMemberMap[nick]; ok { | ||||
| 		if b.nickMemberMap[nick] != nil { | ||||
| 			return b.nickMemberMap[nick], nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil, errors.New("Couldn't find guild member with nick " + nick) // This will most likely get ignored by the caller | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) getChannelID(name string) string { | ||||
| 	idcheck := strings.Split(name, "ID:") | ||||
| 	if len(idcheck) > 1 { | ||||
| 		return idcheck[1] | ||||
| 	} | ||||
| 	for _, channel := range b.Channels { | ||||
| 		if channel.Name == name { | ||||
| 			return channel.ID | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) getChannelName(id string) string { | ||||
| 	for _, channel := range b.Channels { | ||||
| 		if channel.ID == id { | ||||
| 			return channel.Name | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| 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) replaceUserMentions(text string) string { | ||||
| 	re := regexp.MustCompile("@[^@]{1,32}") | ||||
| 	text = re.ReplaceAllStringFunc(text, func(m string) string { | ||||
| 		mention := strings.TrimSpace(m[1:]) | ||||
| 		var member *discordgo.Member | ||||
| 		var err error | ||||
| 		for { | ||||
| 			b.Log.Debugf("Testing mention: '%s'", mention) | ||||
| 			member, err = b.getGuildMemberByNick(mention) | ||||
| 			if err != nil { | ||||
| 				lastSpace := strings.LastIndex(mention, " ") | ||||
| 				if lastSpace == -1 { | ||||
| 					break | ||||
| 				} | ||||
| 				mention = strings.TrimSpace(mention[0:lastSpace]) | ||||
| 			} else { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return m | ||||
| 		} | ||||
| 		return strings.Replace(m, "@"+mention, member.User.Mention(), -1) | ||||
| 	}) | ||||
| 	b.Log.Debugf("Message with mention replaced: %s", text) | ||||
| 	return text | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) replaceAction(text string) (string, bool) { | ||||
| 	if strings.HasPrefix(text, "_") && strings.HasSuffix(text, "_") { | ||||
| 		return strings.Replace(text, "_", "", -1), true | ||||
| 	} | ||||
| 	return text, false | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) stripCustomoji(text string) string { | ||||
| 	// <:doge:302803592035958784> | ||||
| 	re := regexp.MustCompile("<(:.*?:)[0-9]+>") | ||||
| 	return re.ReplaceAllString(text, `$1`) | ||||
| } | ||||
|  | ||||
| // splitURL splits a webhookURL and returns the id and token | ||||
| func (b *Bdiscord) splitURL(url string) (string, string) { | ||||
| 	webhookURLSplit := strings.Split(url, "/") | ||||
| 	if len(webhookURLSplit) != 7 { | ||||
| 		b.Log.Fatalf("%s is no correct discord WebhookURL", url) | ||||
| 	} | ||||
| 	return webhookURLSplit[len(webhookURLSplit)-2], webhookURLSplit[len(webhookURLSplit)-1] | ||||
| } | ||||
|  | ||||
| // useWebhook returns true if we have a webhook defined somewhere | ||||
| func (b *Bdiscord) useWebhook() bool { | ||||
| 	if b.GetString("WebhookURL") != "" { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	b.channelsMutex.RLock() | ||||
| 	defer b.channelsMutex.RUnlock() | ||||
|  | ||||
| 	for _, channel := range b.channelInfoMap { | ||||
| 		if channel.Options.WebhookURL != "" { | ||||
| 			return true | ||||
| @@ -477,6 +268,10 @@ func (b *Bdiscord) isWebhookID(id string) bool { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	b.channelsMutex.RLock() | ||||
| 	defer b.channelsMutex.RUnlock() | ||||
|  | ||||
| 	for _, channel := range b.channelInfoMap { | ||||
| 		if channel.Options.WebhookURL != "" { | ||||
| 			wID, _ := b.splitURL(channel.Options.WebhookURL) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Duco van Amstel
					Duco van Amstel