forked from jshiffer/matterbridge
7c773ebae0
Our Message type has an extra ID field which contains the message ID of the specific bridge. The Send() function has been modified to return a msg ID (after the message to that specific bridge has been created). There is a lru cache of 5000 entries (message IDs). All in memory, so editing messages will only work for messages the bot has seen. Currently we go out from the idea that every message ID is unique, so we don't keep the ID separate for each bridge. (we do for each gateway though) If there's a new message from a bridge, we put that message ID in the LRU cache as key and the []*BrMsgID as value (this slice contains the message ID's of each bridge that received the new message) If there's a new message and this message ID already exists in the cache, it must be an updated message. The value from the cache gets checked for each bridge and if there is a message ID for this bridge, the ID will be added to the Message{} sent to that bridge. If the bridge sees that the ID isn't empty, it'll know it has to update the message with that specific ID instead of creating a new message.
279 lines
7.5 KiB
Go
279 lines
7.5 KiB
Go
package bmattermost
|
|
|
|
import (
|
|
"errors"
|
|
"github.com/42wim/matterbridge/bridge/config"
|
|
"github.com/42wim/matterbridge/matterclient"
|
|
"github.com/42wim/matterbridge/matterhook"
|
|
log "github.com/Sirupsen/logrus"
|
|
"strings"
|
|
)
|
|
|
|
type MMhook struct {
|
|
mh *matterhook.Client
|
|
}
|
|
|
|
type MMapi struct {
|
|
mc *matterclient.MMClient
|
|
mmMap map[string]string
|
|
}
|
|
|
|
type MMMessage struct {
|
|
Text string
|
|
Channel string
|
|
Username string
|
|
UserID string
|
|
ID string
|
|
}
|
|
|
|
type Bmattermost struct {
|
|
MMhook
|
|
MMapi
|
|
Config *config.Protocol
|
|
Remote chan config.Message
|
|
TeamId string
|
|
Account string
|
|
}
|
|
|
|
var flog *log.Entry
|
|
var protocol = "mattermost"
|
|
|
|
func init() {
|
|
flog = log.WithFields(log.Fields{"module": protocol})
|
|
}
|
|
|
|
func New(cfg config.Protocol, account string, c chan config.Message) *Bmattermost {
|
|
b := &Bmattermost{}
|
|
b.Config = &cfg
|
|
b.Remote = c
|
|
b.Account = account
|
|
b.mmMap = make(map[string]string)
|
|
return b
|
|
}
|
|
|
|
func (b *Bmattermost) Command(cmd string) string {
|
|
return ""
|
|
}
|
|
|
|
func (b *Bmattermost) Connect() error {
|
|
if b.Config.WebhookBindAddress != "" {
|
|
if b.Config.WebhookURL != "" {
|
|
flog.Info("Connecting using webhookurl (sending) and webhookbindaddress (receiving)")
|
|
b.mh = matterhook.New(b.Config.WebhookURL,
|
|
matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
|
|
BindAddress: b.Config.WebhookBindAddress})
|
|
} else if b.Config.Token != "" {
|
|
flog.Info("Connecting using token (sending)")
|
|
err := b.apiLogin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if b.Config.Login != "" {
|
|
flog.Info("Connecting using login/password (sending)")
|
|
err := b.apiLogin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
flog.Info("Connecting using webhookbindaddress (receiving)")
|
|
b.mh = matterhook.New(b.Config.WebhookURL,
|
|
matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
|
|
BindAddress: b.Config.WebhookBindAddress})
|
|
}
|
|
go b.handleMatter()
|
|
return nil
|
|
}
|
|
if b.Config.WebhookURL != "" {
|
|
flog.Info("Connecting using webhookurl (sending)")
|
|
b.mh = matterhook.New(b.Config.WebhookURL,
|
|
matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
|
|
DisableServer: true})
|
|
if b.Config.Token != "" {
|
|
flog.Info("Connecting using token (receiving)")
|
|
err := b.apiLogin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go b.handleMatter()
|
|
} else if b.Config.Login != "" {
|
|
flog.Info("Connecting using login/password (receiving)")
|
|
err := b.apiLogin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go b.handleMatter()
|
|
}
|
|
return nil
|
|
} else if b.Config.Token != "" {
|
|
flog.Info("Connecting using token (sending and receiving)")
|
|
err := b.apiLogin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go b.handleMatter()
|
|
} else if b.Config.Login != "" {
|
|
flog.Info("Connecting using login/password (sending and receiving)")
|
|
err := b.apiLogin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go b.handleMatter()
|
|
}
|
|
if b.Config.WebhookBindAddress == "" && b.Config.WebhookURL == "" && b.Config.Login == "" && b.Config.Token == "" {
|
|
return errors.New("No connection method found. See that you have WebhookBindAddress, WebhookURL or Token/Login/Password/Server/Team configured.")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Bmattermost) Disconnect() error {
|
|
return nil
|
|
}
|
|
|
|
func (b *Bmattermost) JoinChannel(channel config.ChannelInfo) error {
|
|
// we can only join channels using the API
|
|
if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" {
|
|
return b.mc.JoinChannel(b.mc.GetChannelId(channel.Name, ""))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
|
flog.Debugf("Receiving %#v", msg)
|
|
if msg.Event == config.EVENT_USER_ACTION {
|
|
msg.Text = "*" + msg.Text + "*"
|
|
}
|
|
nick := msg.Username
|
|
message := msg.Text
|
|
channel := msg.Channel
|
|
|
|
if b.Config.PrefixMessagesWithNick {
|
|
message = nick + message
|
|
}
|
|
if b.Config.WebhookURL != "" {
|
|
matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
|
|
matterMessage.IconURL = msg.Avatar
|
|
matterMessage.Channel = channel
|
|
matterMessage.UserName = nick
|
|
matterMessage.Type = ""
|
|
matterMessage.Text = message
|
|
err := b.mh.Send(matterMessage)
|
|
if err != nil {
|
|
flog.Info(err)
|
|
return "", err
|
|
}
|
|
return "", nil
|
|
}
|
|
if msg.ID != "" {
|
|
return b.mc.EditMessage(msg.ID, message)
|
|
}
|
|
return b.mc.PostMessage(b.mc.GetChannelId(channel, ""), message)
|
|
}
|
|
|
|
func (b *Bmattermost) handleMatter() {
|
|
mchan := make(chan *MMMessage)
|
|
if b.Config.WebhookBindAddress != "" {
|
|
flog.Debugf("Choosing webhooks based receiving")
|
|
go b.handleMatterHook(mchan)
|
|
} else {
|
|
if b.Config.Token != "" {
|
|
flog.Debugf("Choosing token based receiving")
|
|
} else {
|
|
flog.Debugf("Choosing login/password based receiving")
|
|
}
|
|
go b.handleMatterClient(mchan)
|
|
}
|
|
for message := range mchan {
|
|
rmsg := config.Message{Username: message.Username, Channel: message.Channel, Account: b.Account, UserID: message.UserID, ID: message.ID}
|
|
text, ok := b.replaceAction(message.Text)
|
|
if ok {
|
|
rmsg.Event = config.EVENT_USER_ACTION
|
|
}
|
|
rmsg.Text = text
|
|
flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account)
|
|
b.Remote <- rmsg
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
if (message.Raw.Event == "post_edited") && b.Config.EditDisable {
|
|
continue
|
|
}
|
|
// do not post our own messages back to irc
|
|
// only listen to message from our team
|
|
if (message.Raw.Event == "posted" || message.Raw.Event == "post_edited") &&
|
|
b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId {
|
|
// if the message has reactions don't repost it (for now, until we can correlate reaction with message)
|
|
if message.Post.HasReactions {
|
|
continue
|
|
}
|
|
flog.Debugf("Receiving from matterclient %#v", message)
|
|
m := &MMMessage{}
|
|
m.UserID = message.UserID
|
|
m.Username = message.Username
|
|
m.Channel = message.Channel
|
|
m.Text = message.Text
|
|
m.ID = message.Post.Id
|
|
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.GetFileLinks(message.Post.FileIds) {
|
|
m.Text = m.Text + "\n" + link
|
|
}
|
|
}
|
|
mchan <- m
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *Bmattermost) handleMatterHook(mchan chan *MMMessage) {
|
|
for {
|
|
message := b.mh.Receive()
|
|
flog.Debugf("Receiving from matterhook %#v", message)
|
|
m := &MMMessage{}
|
|
m.UserID = message.UserID
|
|
m.Username = message.UserName
|
|
m.Text = message.Text
|
|
m.Channel = message.ChannelName
|
|
mchan <- m
|
|
}
|
|
}
|
|
|
|
func (b *Bmattermost) apiLogin() error {
|
|
password := b.Config.Password
|
|
if b.Config.Token != "" {
|
|
password = "MMAUTHTOKEN=" + b.Config.Token
|
|
}
|
|
|
|
b.mc = matterclient.New(b.Config.Login, password,
|
|
b.Config.Team, b.Config.Server)
|
|
b.mc.SkipTLSVerify = b.Config.SkipTLSVerify
|
|
b.mc.NoTLS = b.Config.NoTLS
|
|
flog.Infof("Connecting %s (team: %s) on %s", b.Config.Login, b.Config.Team, b.Config.Server)
|
|
err := b.mc.Login()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
flog.Info("Connection succeeded")
|
|
b.TeamId = b.mc.GetTeamId()
|
|
go b.mc.WsReceiver()
|
|
go b.mc.StatusLoop()
|
|
return nil
|
|
}
|
|
|
|
func (b *Bmattermost) replaceAction(text string) (string, bool) {
|
|
if strings.HasPrefix(text, "*") && strings.HasSuffix(text, "*") {
|
|
return strings.Replace(text, "*", "", -1), true
|
|
}
|
|
return text, false
|
|
}
|