forked from jshiffer/matterbridge
Converge with matterbridge-plus
This commit is contained in:
parent
0f2976c5ce
commit
aceb830378
437
bridge/bridge.go
Normal file
437
bridge/bridge.go
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"github.com/42wim/matterbridge/matterclient"
|
||||||
|
"github.com/42wim/matterbridge/matterhook"
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/peterhellberg/giphy"
|
||||||
|
ircm "github.com/sorcix/irc"
|
||||||
|
"github.com/thoj/go-ircevent"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//type Bridge struct {
|
||||||
|
type MMhook struct {
|
||||||
|
mh *matterhook.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type MMapi struct {
|
||||||
|
mc *matterclient.MMClient
|
||||||
|
mmMap map[string]string
|
||||||
|
mmIgnoreNicks []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type MMirc struct {
|
||||||
|
i *irc.Connection
|
||||||
|
ircNick string
|
||||||
|
ircMap map[string]string
|
||||||
|
names map[string][]string
|
||||||
|
ircIgnoreNicks []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type MMMessage struct {
|
||||||
|
Text string
|
||||||
|
Channel string
|
||||||
|
Username string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bridge struct {
|
||||||
|
MMhook
|
||||||
|
MMapi
|
||||||
|
MMirc
|
||||||
|
*Config
|
||||||
|
kind string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FancyLog struct {
|
||||||
|
irc *log.Entry
|
||||||
|
mm *log.Entry
|
||||||
|
}
|
||||||
|
|
||||||
|
var flog FancyLog
|
||||||
|
|
||||||
|
const Legacy = "legacy"
|
||||||
|
|
||||||
|
func initFLog() {
|
||||||
|
flog.irc = log.WithFields(log.Fields{"module": "irc"})
|
||||||
|
flog.mm = log.WithFields(log.Fields{"module": "mattermost"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBridge(name string, config *Config, kind string) *Bridge {
|
||||||
|
initFLog()
|
||||||
|
b := &Bridge{}
|
||||||
|
b.Config = config
|
||||||
|
b.kind = kind
|
||||||
|
b.ircNick = b.Config.IRC.Nick
|
||||||
|
b.ircMap = make(map[string]string)
|
||||||
|
b.MMirc.names = make(map[string][]string)
|
||||||
|
b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks)
|
||||||
|
b.mmIgnoreNicks = strings.Fields(b.Config.Mattermost.IgnoreNicks)
|
||||||
|
if kind == Legacy {
|
||||||
|
if len(b.Config.Token) > 0 {
|
||||||
|
for _, val := range b.Config.Token {
|
||||||
|
b.ircMap[val.IRCChannel] = val.MMChannel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.mh = matterhook.New(b.Config.Mattermost.URL,
|
||||||
|
matterhook.Config{Port: b.Config.Mattermost.Port, Token: b.Config.Mattermost.Token,
|
||||||
|
InsecureSkipVerify: b.Config.Mattermost.SkipTLSVerify,
|
||||||
|
BindAddress: b.Config.Mattermost.BindAddress})
|
||||||
|
} else {
|
||||||
|
b.mmMap = make(map[string]string)
|
||||||
|
if len(b.Config.Channel) > 0 {
|
||||||
|
for _, val := range b.Config.Channel {
|
||||||
|
b.ircMap[val.IRC] = val.Mattermost
|
||||||
|
b.mmMap[val.Mattermost] = val.IRC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.mc = matterclient.New(b.Config.Mattermost.Login, b.Config.Mattermost.Password,
|
||||||
|
b.Config.Mattermost.Team, b.Config.Mattermost.Server)
|
||||||
|
b.mc.SkipTLSVerify = b.Config.Mattermost.SkipTLSVerify
|
||||||
|
b.mc.NoTLS = b.Config.Mattermost.NoTLS
|
||||||
|
flog.mm.Infof("Trying login %s (team: %s) on %s", b.Config.Mattermost.Login, b.Config.Mattermost.Team, b.Config.Mattermost.Server)
|
||||||
|
err := b.mc.Login()
|
||||||
|
if err != nil {
|
||||||
|
flog.mm.Fatal("Can not connect", err)
|
||||||
|
}
|
||||||
|
flog.mm.Info("Login ok")
|
||||||
|
b.mc.JoinChannel(b.Config.Mattermost.Channel)
|
||||||
|
if len(b.Config.Channel) > 0 {
|
||||||
|
for _, val := range b.Config.Channel {
|
||||||
|
b.mc.JoinChannel(val.Mattermost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go b.mc.WsReceiver()
|
||||||
|
}
|
||||||
|
flog.irc.Info("Trying IRC connection")
|
||||||
|
b.i = b.createIRC(name)
|
||||||
|
flog.irc.Info("Connection succeeded")
|
||||||
|
go b.handleMatter()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) createIRC(name string) *irc.Connection {
|
||||||
|
i := irc.IRC(b.Config.IRC.Nick, b.Config.IRC.Nick)
|
||||||
|
i.UseTLS = b.Config.IRC.UseTLS
|
||||||
|
i.TLSConfig = &tls.Config{InsecureSkipVerify: b.Config.IRC.SkipTLSVerify}
|
||||||
|
if b.Config.IRC.Password != "" {
|
||||||
|
i.Password = b.Config.IRC.Password
|
||||||
|
}
|
||||||
|
i.AddCallback(ircm.RPL_WELCOME, b.handleNewConnection)
|
||||||
|
i.Connect(b.Config.IRC.Server + ":" + strconv.Itoa(b.Config.IRC.Port))
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) handleNewConnection(event *irc.Event) {
|
||||||
|
flog.irc.Info("Registering callbacks")
|
||||||
|
i := b.i
|
||||||
|
b.ircNick = event.Arguments[0]
|
||||||
|
i.AddCallback("PRIVMSG", b.handlePrivMsg)
|
||||||
|
i.AddCallback("CTCP_ACTION", b.handlePrivMsg)
|
||||||
|
i.AddCallback(ircm.RPL_ENDOFNAMES, b.endNames)
|
||||||
|
i.AddCallback(ircm.RPL_NAMREPLY, b.storeNames)
|
||||||
|
i.AddCallback(ircm.RPL_TOPICWHOTIME, b.handleTopicWhoTime)
|
||||||
|
i.AddCallback(ircm.NOTICE, b.handleNotice)
|
||||||
|
i.AddCallback(ircm.RPL_MYINFO, func(e *irc.Event) { flog.irc.Infof("%s: %s", e.Code, strings.Join(e.Arguments[1:], " ")) })
|
||||||
|
i.AddCallback("PING", func(e *irc.Event) {
|
||||||
|
i.SendRaw("PONG :" + e.Message())
|
||||||
|
flog.irc.Debugf("PING/PONG")
|
||||||
|
})
|
||||||
|
if b.Config.Mattermost.ShowJoinPart {
|
||||||
|
i.AddCallback("JOIN", b.handleJoinPart)
|
||||||
|
i.AddCallback("PART", b.handleJoinPart)
|
||||||
|
}
|
||||||
|
i.AddCallback("*", b.handleOther)
|
||||||
|
b.setupChannels()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) setupChannels() {
|
||||||
|
i := b.i
|
||||||
|
if b.Config.IRC.Channel != "" {
|
||||||
|
flog.irc.Infof("Joining %s as %s", b.Config.IRC.Channel, b.ircNick)
|
||||||
|
i.Join(b.Config.IRC.Channel)
|
||||||
|
}
|
||||||
|
if b.kind == Legacy {
|
||||||
|
for _, val := range b.Config.Token {
|
||||||
|
flog.irc.Infof("Joining %s as %s", val.IRCChannel, b.ircNick)
|
||||||
|
i.Join(val.IRCChannel)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, val := range b.Config.Channel {
|
||||||
|
flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick)
|
||||||
|
i.Join(val.IRC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) handleIrcBotCommand(event *irc.Event) bool {
|
||||||
|
parts := strings.Fields(event.Message())
|
||||||
|
exp, _ := regexp.Compile("[:,]+$")
|
||||||
|
channel := event.Arguments[0]
|
||||||
|
command := ""
|
||||||
|
if len(parts) == 2 {
|
||||||
|
command = parts[1]
|
||||||
|
}
|
||||||
|
if exp.ReplaceAllString(parts[0], "") == b.ircNick {
|
||||||
|
switch command {
|
||||||
|
case "users":
|
||||||
|
usernames := b.mc.UsernamesInChannel(b.getMMChannel(channel))
|
||||||
|
sort.Strings(usernames)
|
||||||
|
b.i.Privmsg(channel, "Users on Mattermost: "+strings.Join(usernames, ", "))
|
||||||
|
default:
|
||||||
|
b.i.Privmsg(channel, "Valid commands are: [users, help]")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) ircNickFormat(nick string) string {
|
||||||
|
if nick == b.ircNick {
|
||||||
|
return nick
|
||||||
|
}
|
||||||
|
if b.Config.Mattermost.RemoteNickFormat == nil {
|
||||||
|
return "irc-" + nick
|
||||||
|
}
|
||||||
|
return strings.Replace(*b.Config.Mattermost.RemoteNickFormat, "{NICK}", nick, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) handlePrivMsg(event *irc.Event) {
|
||||||
|
if b.ignoreMessage(event.Nick, event.Message(), "irc") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b.handleIrcBotCommand(event) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg := ""
|
||||||
|
if event.Code == "CTCP_ACTION" {
|
||||||
|
msg = event.Nick + " "
|
||||||
|
}
|
||||||
|
msg += event.Message()
|
||||||
|
b.Send(b.ircNickFormat(event.Nick), msg, b.getMMChannel(event.Arguments[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) handleJoinPart(event *irc.Event) {
|
||||||
|
b.Send(b.ircNick, b.ircNickFormat(event.Nick)+" "+strings.ToLower(event.Code)+"s "+event.Message(), b.getMMChannel(event.Arguments[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) handleNotice(event *irc.Event) {
|
||||||
|
if strings.Contains(event.Message(), "This nickname is registered") {
|
||||||
|
b.i.Privmsg(b.Config.IRC.NickServNick, "IDENTIFY "+b.Config.IRC.NickServPassword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) nicksPerRow() int {
|
||||||
|
if b.Config.Mattermost.NicksPerRow < 1 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
return b.Config.Mattermost.NicksPerRow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) formatnicks(nicks []string, continued bool) string {
|
||||||
|
switch b.Config.Mattermost.NickFormatter {
|
||||||
|
case "table":
|
||||||
|
return tableformatter(nicks, b.nicksPerRow(), continued)
|
||||||
|
default:
|
||||||
|
return plainformatter(nicks, b.nicksPerRow())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) storeNames(event *irc.Event) {
|
||||||
|
channel := event.Arguments[2]
|
||||||
|
b.MMirc.names[channel] = append(
|
||||||
|
b.MMirc.names[channel],
|
||||||
|
strings.Split(strings.TrimSpace(event.Message()), " ")...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) endNames(event *irc.Event) {
|
||||||
|
channel := event.Arguments[1]
|
||||||
|
sort.Strings(b.MMirc.names[channel])
|
||||||
|
maxNamesPerPost := (300 / b.nicksPerRow()) * b.nicksPerRow()
|
||||||
|
continued := false
|
||||||
|
for len(b.MMirc.names[channel]) > maxNamesPerPost {
|
||||||
|
b.Send(
|
||||||
|
b.ircNick,
|
||||||
|
b.formatnicks(b.MMirc.names[channel][0:maxNamesPerPost], continued),
|
||||||
|
b.getMMChannel(channel))
|
||||||
|
b.MMirc.names[channel] = b.MMirc.names[channel][maxNamesPerPost:]
|
||||||
|
continued = true
|
||||||
|
}
|
||||||
|
b.Send(b.ircNick, b.formatnicks(b.MMirc.names[channel], continued), b.getMMChannel(channel))
|
||||||
|
b.MMirc.names[channel] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) handleTopicWhoTime(event *irc.Event) {
|
||||||
|
parts := strings.Split(event.Arguments[2], "!")
|
||||||
|
t, err := strconv.ParseInt(event.Arguments[3], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
flog.irc.Errorf("Invalid time stamp: %s", event.Arguments[3])
|
||||||
|
}
|
||||||
|
user := parts[0]
|
||||||
|
if len(parts) > 1 {
|
||||||
|
user += " [" + parts[1] + "]"
|
||||||
|
}
|
||||||
|
flog.irc.Infof("%s: Topic set by %s [%s]", event.Code, user, time.Unix(t, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) handleOther(event *irc.Event) {
|
||||||
|
flog.irc.Debugf("%#v", event)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) Send(nick string, message string, channel string) error {
|
||||||
|
return b.SendType(nick, message, channel, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) SendType(nick string, message string, channel string, mtype string) error {
|
||||||
|
if b.Config.Mattermost.PrefixMessagesWithNick {
|
||||||
|
if IsMarkup(message) {
|
||||||
|
message = nick + "\n\n" + message
|
||||||
|
} else {
|
||||||
|
message = nick + " " + message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b.kind == Legacy {
|
||||||
|
matterMessage := matterhook.OMessage{IconURL: b.Config.Mattermost.IconURL}
|
||||||
|
matterMessage.Channel = channel
|
||||||
|
matterMessage.UserName = nick
|
||||||
|
matterMessage.Type = mtype
|
||||||
|
matterMessage.Text = message
|
||||||
|
err := b.mh.Send(matterMessage)
|
||||||
|
if err != nil {
|
||||||
|
flog.mm.Info(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
flog.mm.Debug("->mattermost channel: ", channel, " ", message)
|
||||||
|
b.mc.PostMessage(channel, message)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) handleMatterHook(mchan chan *MMMessage) {
|
||||||
|
for {
|
||||||
|
message := b.mh.Receive()
|
||||||
|
m := &MMMessage{}
|
||||||
|
m.Username = message.UserName
|
||||||
|
m.Text = message.Text
|
||||||
|
m.Channel = message.Token
|
||||||
|
mchan <- m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) handleMatterClient(mchan chan *MMMessage) {
|
||||||
|
for message := range b.mc.MessageChan {
|
||||||
|
// do not post our own messages back to irc
|
||||||
|
if message.Raw.Action == "posted" && b.mc.User.Username != message.Username {
|
||||||
|
m := &MMMessage{}
|
||||||
|
m.Username = message.Username
|
||||||
|
m.Channel = message.Channel
|
||||||
|
m.Text = message.Text
|
||||||
|
flog.mm.Debugf("<-mattermost channel: %s %#v %#v", message.Channel, message.Post, message.Raw)
|
||||||
|
mchan <- m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) handleMatter() {
|
||||||
|
flog.mm.Infof("Choosing Mattermost connection type %s", b.kind)
|
||||||
|
mchan := make(chan *MMMessage)
|
||||||
|
if b.kind == Legacy {
|
||||||
|
go b.handleMatterHook(mchan)
|
||||||
|
} else {
|
||||||
|
go b.handleMatterClient(mchan)
|
||||||
|
}
|
||||||
|
flog.mm.Info("Start listening for Mattermost messages")
|
||||||
|
for message := range mchan {
|
||||||
|
var username string
|
||||||
|
if b.ignoreMessage(message.Username, message.Text, "mattermost") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
username = message.Username + ": "
|
||||||
|
if b.Config.IRC.RemoteNickFormat != "" {
|
||||||
|
username = strings.Replace(b.Config.IRC.RemoteNickFormat, "{NICK}", message.Username, -1)
|
||||||
|
} else if b.Config.IRC.UseSlackCircumfix {
|
||||||
|
username = "<" + message.Username + "> "
|
||||||
|
}
|
||||||
|
cmds := strings.Fields(message.Text)
|
||||||
|
// empty message
|
||||||
|
if len(cmds) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cmd := cmds[0]
|
||||||
|
switch cmd {
|
||||||
|
case "!users":
|
||||||
|
flog.mm.Info("Received !users from ", message.Username)
|
||||||
|
b.i.SendRaw("NAMES " + b.getIRCChannel(message.Channel))
|
||||||
|
continue
|
||||||
|
case "!gif":
|
||||||
|
message.Text = b.giphyRandom(strings.Fields(strings.Replace(message.Text, "!gif ", "", 1)))
|
||||||
|
b.Send(b.ircNick, message.Text, b.getIRCChannel(message.Channel))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
texts := strings.Split(message.Text, "\n")
|
||||||
|
for _, text := range texts {
|
||||||
|
flog.mm.Debug("Sending message from " + message.Username + " to " + message.Channel)
|
||||||
|
b.i.Privmsg(b.getIRCChannel(message.Channel), username+text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) giphyRandom(query []string) string {
|
||||||
|
g := giphy.DefaultClient
|
||||||
|
if b.Config.General.GiphyAPIKey != "" {
|
||||||
|
g.APIKey = b.Config.General.GiphyAPIKey
|
||||||
|
}
|
||||||
|
res, err := g.Random(query)
|
||||||
|
if err != nil {
|
||||||
|
return "error"
|
||||||
|
}
|
||||||
|
return res.Data.FixedHeightDownsampledURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) getMMChannel(ircChannel string) string {
|
||||||
|
mmchannel, ok := b.ircMap[ircChannel]
|
||||||
|
if !ok {
|
||||||
|
mmchannel = b.Config.Mattermost.Channel
|
||||||
|
}
|
||||||
|
if b.kind == Legacy {
|
||||||
|
return mmchannel
|
||||||
|
}
|
||||||
|
return b.mc.GetChannelId(mmchannel, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) getIRCChannel(channel string) string {
|
||||||
|
if b.kind == Legacy {
|
||||||
|
ircchannel := b.Config.IRC.Channel
|
||||||
|
_, ok := b.Config.Token[channel]
|
||||||
|
if ok {
|
||||||
|
ircchannel = b.Config.Token[channel].IRCChannel
|
||||||
|
}
|
||||||
|
return ircchannel
|
||||||
|
}
|
||||||
|
ircchannel, ok := b.mmMap[channel]
|
||||||
|
if !ok {
|
||||||
|
ircchannel = b.Config.IRC.Channel
|
||||||
|
}
|
||||||
|
return ircchannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) ignoreMessage(nick string, message string, protocol string) bool {
|
||||||
|
var ignoreNicks = b.mmIgnoreNicks
|
||||||
|
if protocol == "irc" {
|
||||||
|
ignoreNicks = b.ircIgnoreNicks
|
||||||
|
}
|
||||||
|
// should we discard messages ?
|
||||||
|
for _, entry := range ignoreNicks {
|
||||||
|
if nick == entry {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
68
bridge/config.go
Normal file
68
bridge/config.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gopkg.in/gcfg.v1"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
IRC struct {
|
||||||
|
UseTLS bool
|
||||||
|
SkipTLSVerify bool
|
||||||
|
Server string
|
||||||
|
Port int
|
||||||
|
Nick string
|
||||||
|
Password string
|
||||||
|
Channel string
|
||||||
|
UseSlackCircumfix bool
|
||||||
|
NickServNick string
|
||||||
|
NickServPassword string
|
||||||
|
RemoteNickFormat string
|
||||||
|
IgnoreNicks string
|
||||||
|
}
|
||||||
|
Mattermost struct {
|
||||||
|
URL string
|
||||||
|
Port int
|
||||||
|
ShowJoinPart bool
|
||||||
|
Token string
|
||||||
|
IconURL string
|
||||||
|
SkipTLSVerify bool
|
||||||
|
BindAddress string
|
||||||
|
Channel string
|
||||||
|
PrefixMessagesWithNick bool
|
||||||
|
NicksPerRow int
|
||||||
|
NickFormatter string
|
||||||
|
Server string
|
||||||
|
Team string
|
||||||
|
Login string
|
||||||
|
Password string
|
||||||
|
RemoteNickFormat *string
|
||||||
|
IgnoreNicks string
|
||||||
|
NoTLS bool
|
||||||
|
}
|
||||||
|
Token map[string]*struct {
|
||||||
|
IRCChannel string
|
||||||
|
MMChannel string
|
||||||
|
}
|
||||||
|
Channel map[string]*struct {
|
||||||
|
IRC string
|
||||||
|
Mattermost string
|
||||||
|
}
|
||||||
|
General struct {
|
||||||
|
GiphyAPIKey string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfig(cfgfile string) *Config {
|
||||||
|
var cfg Config
|
||||||
|
content, err := ioutil.ReadFile(cfgfile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = gcfg.ReadStringInto(&cfg, string(content))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to parse "+cfgfile+":", err)
|
||||||
|
}
|
||||||
|
return &cfg
|
||||||
|
}
|
59
bridge/helper.go
Normal file
59
bridge/helper.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tableformatter(nicks []string, nicksPerRow int, continued bool) string {
|
||||||
|
result := "|IRC users"
|
||||||
|
if continued {
|
||||||
|
result = "|(continued)"
|
||||||
|
}
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
for j := 1; j <= nicksPerRow && j <= len(nicks); j++ {
|
||||||
|
if i == 0 {
|
||||||
|
result += "|"
|
||||||
|
} else {
|
||||||
|
result += ":-|"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += "\r\n|"
|
||||||
|
}
|
||||||
|
result += nicks[0] + "|"
|
||||||
|
for i := 1; i < len(nicks); i++ {
|
||||||
|
if i%nicksPerRow == 0 {
|
||||||
|
result += "\r\n|" + nicks[i] + "|"
|
||||||
|
} else {
|
||||||
|
result += nicks[i] + "|"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func plainformatter(nicks []string, nicksPerRow int) string {
|
||||||
|
return strings.Join(nicks, ", ") + " currently on IRC"
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsMarkup(message string) bool {
|
||||||
|
switch message[0] {
|
||||||
|
case '|':
|
||||||
|
fallthrough
|
||||||
|
case '#':
|
||||||
|
fallthrough
|
||||||
|
case '_':
|
||||||
|
fallthrough
|
||||||
|
case '*':
|
||||||
|
fallthrough
|
||||||
|
case '~':
|
||||||
|
fallthrough
|
||||||
|
case '-':
|
||||||
|
fallthrough
|
||||||
|
case ':':
|
||||||
|
fallthrough
|
||||||
|
case '>':
|
||||||
|
fallthrough
|
||||||
|
case '=':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/42wim/matterbridge-plus/bridge"
|
"github.com/42wim/matterbridge/bridge"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,6 +17,7 @@ func main() {
|
|||||||
flagConfig := flag.String("conf", "matterbridge.conf", "config file")
|
flagConfig := flag.String("conf", "matterbridge.conf", "config file")
|
||||||
flagDebug := flag.Bool("debug", false, "enable debug")
|
flagDebug := flag.Bool("debug", false, "enable debug")
|
||||||
flagVersion := flag.Bool("version", false, "show version")
|
flagVersion := flag.Bool("version", false, "show version")
|
||||||
|
flagPlus := flag.Bool("plus", false, "running using API instead of webhooks")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if *flagVersion {
|
if *flagVersion {
|
||||||
fmt.Println("Version:", Version)
|
fmt.Println("Version:", Version)
|
||||||
@ -28,6 +29,10 @@ func main() {
|
|||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
}
|
}
|
||||||
fmt.Println("running version", Version)
|
fmt.Println("running version", Version)
|
||||||
|
if *flagPlus {
|
||||||
|
bridge.NewBridge("matterbot", bridge.NewConfig(*flagConfig), "")
|
||||||
|
} else {
|
||||||
bridge.NewBridge("matterbot", bridge.NewConfig(*flagConfig), "legacy")
|
bridge.NewBridge("matterbot", bridge.NewConfig(*flagConfig), "legacy")
|
||||||
|
}
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
570
matterclient/matterclient.go
Normal file
570
matterclient/matterclient.go
Normal file
@ -0,0 +1,570 @@
|
|||||||
|
package matterclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/http/cookiejar"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/jpillora/backoff"
|
||||||
|
"github.com/mattermost/platform/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Credentials struct {
|
||||||
|
Login string
|
||||||
|
Team string
|
||||||
|
Pass string
|
||||||
|
Server string
|
||||||
|
NoTLS bool
|
||||||
|
SkipTLSVerify bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Raw *model.Message
|
||||||
|
Post *model.Post
|
||||||
|
Team string
|
||||||
|
Channel string
|
||||||
|
Username string
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Team struct {
|
||||||
|
Team *model.Team
|
||||||
|
Id string
|
||||||
|
Channels *model.ChannelList
|
||||||
|
MoreChannels *model.ChannelList
|
||||||
|
Users map[string]*model.User
|
||||||
|
}
|
||||||
|
|
||||||
|
type MMClient struct {
|
||||||
|
sync.RWMutex
|
||||||
|
*Credentials
|
||||||
|
Team *Team
|
||||||
|
OtherTeams []*Team
|
||||||
|
Client *model.Client
|
||||||
|
WsClient *websocket.Conn
|
||||||
|
WsQuit bool
|
||||||
|
WsAway bool
|
||||||
|
WsConnected bool
|
||||||
|
User *model.User
|
||||||
|
Users map[string]*model.User
|
||||||
|
MessageChan chan *Message
|
||||||
|
log *log.Entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(login, pass, team, server string) *MMClient {
|
||||||
|
cred := &Credentials{Login: login, Pass: pass, Team: team, Server: server}
|
||||||
|
mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100), Users: make(map[string]*model.User)}
|
||||||
|
mmclient.log = log.WithFields(log.Fields{"module": "matterclient"})
|
||||||
|
log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
|
||||||
|
return mmclient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) SetLogLevel(level string) {
|
||||||
|
l, err := log.ParseLevel(level)
|
||||||
|
if err != nil {
|
||||||
|
log.SetLevel(log.InfoLevel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.SetLevel(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) Login() error {
|
||||||
|
m.WsConnected = false
|
||||||
|
if m.WsQuit {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
b := &backoff.Backoff{
|
||||||
|
Min: time.Second,
|
||||||
|
Max: 5 * time.Minute,
|
||||||
|
Jitter: true,
|
||||||
|
}
|
||||||
|
uriScheme := "https://"
|
||||||
|
wsScheme := "wss://"
|
||||||
|
if m.NoTLS {
|
||||||
|
uriScheme = "http://"
|
||||||
|
wsScheme = "ws://"
|
||||||
|
}
|
||||||
|
// login to mattermost
|
||||||
|
m.Client = model.NewClient(uriScheme + m.Credentials.Server)
|
||||||
|
m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
|
||||||
|
var myinfo *model.Result
|
||||||
|
var appErr *model.AppError
|
||||||
|
var logmsg = "trying login"
|
||||||
|
for {
|
||||||
|
m.log.Debugf("%s %s %s %s", logmsg, m.Credentials.Team, m.Credentials.Login, m.Credentials.Server)
|
||||||
|
if strings.Contains(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN) {
|
||||||
|
m.log.Debugf(logmsg+" with %s", model.SESSION_COOKIE_TOKEN)
|
||||||
|
token := strings.Split(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN+"=")
|
||||||
|
if len(token) != 2 {
|
||||||
|
return errors.New("incorrect MMAUTHTOKEN. valid input is MMAUTHTOKEN=yourtoken")
|
||||||
|
}
|
||||||
|
m.Client.HttpClient.Jar = m.createCookieJar(token[1])
|
||||||
|
m.Client.MockSession(token[1])
|
||||||
|
myinfo, appErr = m.Client.GetMe("")
|
||||||
|
if appErr != nil {
|
||||||
|
return errors.New(appErr.DetailedError)
|
||||||
|
}
|
||||||
|
if myinfo.Data.(*model.User) == nil {
|
||||||
|
m.log.Errorf("LOGIN TOKEN: %s is invalid", m.Credentials.Pass)
|
||||||
|
return errors.New("invalid " + model.SESSION_COOKIE_TOKEN)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
myinfo, appErr = m.Client.Login(m.Credentials.Login, m.Credentials.Pass)
|
||||||
|
}
|
||||||
|
if appErr != nil {
|
||||||
|
d := b.Duration()
|
||||||
|
m.log.Debug(appErr.DetailedError)
|
||||||
|
if !strings.Contains(appErr.DetailedError, "connection refused") &&
|
||||||
|
!strings.Contains(appErr.DetailedError, "invalid character") {
|
||||||
|
if appErr.Message == "" {
|
||||||
|
return errors.New(appErr.DetailedError)
|
||||||
|
}
|
||||||
|
return errors.New(appErr.Message)
|
||||||
|
}
|
||||||
|
m.log.Debugf("LOGIN: %s, reconnecting in %s", appErr, d)
|
||||||
|
time.Sleep(d)
|
||||||
|
logmsg = "retrying login"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// reset timer
|
||||||
|
b.Reset()
|
||||||
|
|
||||||
|
err := m.initUser()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set our team id as default route
|
||||||
|
m.Client.SetTeamId(m.Team.Id)
|
||||||
|
if m.Team == nil {
|
||||||
|
return errors.New("team not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup websocket connection
|
||||||
|
wsurl := wsScheme + m.Credentials.Server + "/api/v3/users/websocket"
|
||||||
|
header := http.Header{}
|
||||||
|
header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
|
||||||
|
|
||||||
|
m.log.Debug("WsClient: making connection")
|
||||||
|
for {
|
||||||
|
wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
|
||||||
|
m.WsClient, _, err = wsDialer.Dial(wsurl, header)
|
||||||
|
if err != nil {
|
||||||
|
d := b.Duration()
|
||||||
|
m.log.Debugf("WSS: %s, reconnecting in %s", err, d)
|
||||||
|
time.Sleep(d)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b.Reset()
|
||||||
|
|
||||||
|
// only start to parse WS messages when login is completely done
|
||||||
|
m.WsConnected = true
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) Logout() error {
|
||||||
|
m.log.Debugf("logout as %s (team: %s) on %s", m.Credentials.Login, m.Credentials.Team, m.Credentials.Server)
|
||||||
|
m.WsQuit = true
|
||||||
|
m.WsClient.Close()
|
||||||
|
m.WsClient.UnderlyingConn().Close()
|
||||||
|
m.WsClient = nil
|
||||||
|
_, err := m.Client.Logout()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) WsReceiver() {
|
||||||
|
var rmsg model.Message
|
||||||
|
for {
|
||||||
|
if m.WsQuit {
|
||||||
|
m.log.Debug("exiting WsReceiver")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := m.WsClient.ReadJSON(&rmsg); err != nil {
|
||||||
|
m.log.Error("error:", err)
|
||||||
|
// reconnect
|
||||||
|
m.Login()
|
||||||
|
}
|
||||||
|
// we're not fully logged in yet.
|
||||||
|
if !m.WsConnected {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rmsg.Action == "ping" {
|
||||||
|
m.handleWsPing()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
msg := &Message{Raw: &rmsg, Team: m.Credentials.Team}
|
||||||
|
m.parseMessage(msg)
|
||||||
|
m.MessageChan <- msg
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) handleWsPing() {
|
||||||
|
m.log.Debug("Ws PING")
|
||||||
|
if !m.WsQuit && !m.WsAway {
|
||||||
|
m.log.Debug("Ws PONG")
|
||||||
|
m.WsClient.WriteMessage(websocket.PongMessage, []byte{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) parseMessage(rmsg *Message) {
|
||||||
|
switch rmsg.Raw.Action {
|
||||||
|
case model.ACTION_POSTED:
|
||||||
|
m.parseActionPost(rmsg)
|
||||||
|
/*
|
||||||
|
case model.ACTION_USER_REMOVED:
|
||||||
|
m.handleWsActionUserRemoved(&rmsg)
|
||||||
|
case model.ACTION_USER_ADDED:
|
||||||
|
m.handleWsActionUserAdded(&rmsg)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) parseActionPost(rmsg *Message) {
|
||||||
|
data := model.PostFromJson(strings.NewReader(rmsg.Raw.Props["post"]))
|
||||||
|
// we don't have the user, refresh the userlist
|
||||||
|
if m.GetUser(data.UserId) == nil {
|
||||||
|
m.UpdateUsers()
|
||||||
|
}
|
||||||
|
rmsg.Username = m.GetUser(data.UserId).Username
|
||||||
|
rmsg.Channel = m.GetChannelName(data.ChannelId)
|
||||||
|
rmsg.Team = m.GetTeamName(rmsg.Raw.TeamId)
|
||||||
|
// direct message
|
||||||
|
if data.Type == "D" {
|
||||||
|
rmsg.Channel = m.GetUser(data.UserId).Username
|
||||||
|
}
|
||||||
|
rmsg.Text = data.Message
|
||||||
|
rmsg.Post = data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) UpdateUsers() error {
|
||||||
|
mmusers, _ := m.Client.GetProfilesForDirectMessageList(m.Team.Id)
|
||||||
|
m.Lock()
|
||||||
|
m.Users = mmusers.Data.(map[string]*model.User)
|
||||||
|
m.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) UpdateChannels() error {
|
||||||
|
mmchannels, _ := m.Client.GetChannels("")
|
||||||
|
mmchannels2, _ := m.Client.GetMoreChannels("")
|
||||||
|
m.Lock()
|
||||||
|
m.Team.Channels = mmchannels.Data.(*model.ChannelList)
|
||||||
|
m.Team.MoreChannels = mmchannels2.Data.(*model.ChannelList)
|
||||||
|
m.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) GetChannelName(channelId string) string {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
for _, t := range m.OtherTeams {
|
||||||
|
for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) {
|
||||||
|
if channel.Id == channelId {
|
||||||
|
return channel.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) GetChannelId(name string, teamId string) string {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
if teamId == "" {
|
||||||
|
teamId = m.Team.Id
|
||||||
|
}
|
||||||
|
for _, t := range m.OtherTeams {
|
||||||
|
if t.Id == teamId {
|
||||||
|
for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) {
|
||||||
|
if channel.Name == name {
|
||||||
|
return channel.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) GetChannelHeader(channelId string) string {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
for _, t := range m.OtherTeams {
|
||||||
|
for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) {
|
||||||
|
if channel.Id == channelId {
|
||||||
|
return channel.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) PostMessage(channelId string, text string) {
|
||||||
|
post := &model.Post{ChannelId: channelId, Message: text}
|
||||||
|
m.Client.CreatePost(post)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) JoinChannel(channelId string) error {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
for _, c := range m.Team.Channels.Channels {
|
||||||
|
if c.Id == channelId {
|
||||||
|
m.log.Debug("Not joining ", channelId, " already joined.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.log.Debug("Joining ", channelId)
|
||||||
|
_, err := m.Client.JoinChannel(channelId)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("failed to join")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) GetPostsSince(channelId string, time int64) *model.PostList {
|
||||||
|
res, err := m.Client.GetPostsSince(channelId, time)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return res.Data.(*model.PostList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) SearchPosts(query string) *model.PostList {
|
||||||
|
res, err := m.Client.SearchPosts(query, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return res.Data.(*model.PostList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList {
|
||||||
|
res, err := m.Client.GetPosts(channelId, 0, limit, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return res.Data.(*model.PostList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) GetPublicLink(filename string) string {
|
||||||
|
res, err := m.Client.GetPublicLink(filename)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return res.Data.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) GetPublicLinks(filenames []string) []string {
|
||||||
|
var output []string
|
||||||
|
for _, f := range filenames {
|
||||||
|
res, err := m.Client.GetPublicLink(f)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
output = append(output, res.Data.(string))
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) UpdateChannelHeader(channelId string, header string) {
|
||||||
|
data := make(map[string]string)
|
||||||
|
data["channel_id"] = channelId
|
||||||
|
data["channel_header"] = header
|
||||||
|
m.log.Debugf("updating channelheader %#v, %#v", channelId, header)
|
||||||
|
_, err := m.Client.UpdateChannelHeader(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) UpdateLastViewed(channelId string) {
|
||||||
|
m.log.Debugf("posting lastview %#v", channelId)
|
||||||
|
_, err := m.Client.UpdateLastViewedAt(channelId)
|
||||||
|
if err != nil {
|
||||||
|
m.log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) UsernamesInChannel(channelId string) []string {
|
||||||
|
ceiRes, err := m.Client.GetChannelExtraInfo(channelId, 5000, "")
|
||||||
|
if err != nil {
|
||||||
|
m.log.Errorf("UsernamesInChannel(%s) failed: %s", channelId, err)
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
extra := ceiRes.Data.(*model.ChannelExtra)
|
||||||
|
result := []string{}
|
||||||
|
for _, member := range extra.Members {
|
||||||
|
result = append(result, member.Username)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) createCookieJar(token string) *cookiejar.Jar {
|
||||||
|
var cookies []*http.Cookie
|
||||||
|
jar, _ := cookiejar.New(nil)
|
||||||
|
firstCookie := &http.Cookie{
|
||||||
|
Name: "MMAUTHTOKEN",
|
||||||
|
Value: token,
|
||||||
|
Path: "/",
|
||||||
|
Domain: m.Credentials.Server,
|
||||||
|
}
|
||||||
|
cookies = append(cookies, firstCookie)
|
||||||
|
cookieURL, _ := url.Parse("https://" + m.Credentials.Server)
|
||||||
|
jar.SetCookies(cookieURL, cookies)
|
||||||
|
return jar
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendDirectMessage sends a direct message to specified user
|
||||||
|
func (m *MMClient) SendDirectMessage(toUserId string, msg string) {
|
||||||
|
m.log.Debugf("SendDirectMessage to %s, msg %s", toUserId, msg)
|
||||||
|
// create DM channel (only happens on first message)
|
||||||
|
_, err := m.Client.CreateDirectChannel(toUserId)
|
||||||
|
if err != nil {
|
||||||
|
m.log.Debugf("SendDirectMessage to %#v failed: %s", toUserId, err)
|
||||||
|
}
|
||||||
|
channelName := model.GetDMNameFromIds(toUserId, m.User.Id)
|
||||||
|
|
||||||
|
// update our channels
|
||||||
|
mmchannels, _ := m.Client.GetChannels("")
|
||||||
|
m.Lock()
|
||||||
|
m.Team.Channels = mmchannels.Data.(*model.ChannelList)
|
||||||
|
m.Unlock()
|
||||||
|
|
||||||
|
// build & send the message
|
||||||
|
msg = strings.Replace(msg, "\r", "", -1)
|
||||||
|
post := &model.Post{ChannelId: m.GetChannelId(channelName, ""), Message: msg}
|
||||||
|
m.Client.CreatePost(post)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTeamName returns the name of the specified teamId
|
||||||
|
func (m *MMClient) GetTeamName(teamId string) string {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
for _, t := range m.OtherTeams {
|
||||||
|
if t.Id == teamId {
|
||||||
|
return t.Team.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChannels returns all channels we're members off
|
||||||
|
func (m *MMClient) GetChannels() []*model.Channel {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
var channels []*model.Channel
|
||||||
|
// our primary team channels first
|
||||||
|
channels = append(channels, m.Team.Channels.Channels...)
|
||||||
|
for _, t := range m.OtherTeams {
|
||||||
|
if t.Id != m.Team.Id {
|
||||||
|
channels = append(channels, t.Channels.Channels...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMoreChannels returns existing channels where we're not a member off.
|
||||||
|
func (m *MMClient) GetMoreChannels() []*model.Channel {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
var channels []*model.Channel
|
||||||
|
for _, t := range m.OtherTeams {
|
||||||
|
channels = append(channels, t.MoreChannels.Channels...)
|
||||||
|
}
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTeamFromChannel returns teamId belonging to channel (DM channels have no teamId).
|
||||||
|
func (m *MMClient) GetTeamFromChannel(channelId string) string {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
var channels []*model.Channel
|
||||||
|
for _, t := range m.OtherTeams {
|
||||||
|
channels = append(channels, t.Channels.Channels...)
|
||||||
|
for _, c := range channels {
|
||||||
|
if c.Id == channelId {
|
||||||
|
return t.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) GetLastViewedAt(channelId string) int64 {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
for _, t := range m.OtherTeams {
|
||||||
|
if _, ok := t.Channels.Members[channelId]; ok {
|
||||||
|
return t.Channels.Members[channelId].LastViewedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) GetUsers() map[string]*model.User {
|
||||||
|
users := make(map[string]*model.User)
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
for k, v := range m.Users {
|
||||||
|
users[k] = v
|
||||||
|
}
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) GetUser(userId string) *model.User {
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
return m.Users[userId]
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize user and teams
|
||||||
|
func (m *MMClient) initUser() error {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
m.log.Debug("initUser()")
|
||||||
|
initLoad, err := m.Client.GetInitialLoad()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
initData := initLoad.Data.(*model.InitialLoad)
|
||||||
|
m.User = initData.User
|
||||||
|
// we only load all team data on initial login.
|
||||||
|
// all other updates are for channels from our (primary) team only.
|
||||||
|
m.log.Debug("initUser(): loading all team data")
|
||||||
|
for _, v := range initData.Teams {
|
||||||
|
m.Client.SetTeamId(v.Id)
|
||||||
|
mmusers, _ := m.Client.GetProfiles(v.Id, "")
|
||||||
|
t := &Team{Team: v, Users: mmusers.Data.(map[string]*model.User), Id: v.Id}
|
||||||
|
mmchannels, _ := m.Client.GetChannels("")
|
||||||
|
t.Channels = mmchannels.Data.(*model.ChannelList)
|
||||||
|
mmchannels, _ = m.Client.GetMoreChannels("")
|
||||||
|
t.MoreChannels = mmchannels.Data.(*model.ChannelList)
|
||||||
|
m.OtherTeams = append(m.OtherTeams, t)
|
||||||
|
if v.Name == m.Credentials.Team {
|
||||||
|
m.Team = t
|
||||||
|
m.log.Debugf("initUser(): found our team %s (id: %s)", v.Name, v.Id)
|
||||||
|
}
|
||||||
|
// add all users
|
||||||
|
for k, v := range t.Users {
|
||||||
|
m.Users[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user