matterbridge/matterclient/matterclient.go

239 lines
5.3 KiB
Go
Raw Normal View History

2016-07-11 12:23:33 -07:00
package matterclient
import (
"encoding/json"
2017-04-09 14:15:11 -07:00
"fmt"
2016-07-11 12:23:33 -07:00
"strings"
"sync"
"time"
"github.com/gorilla/websocket"
"github.com/hashicorp/golang-lru"
2016-07-11 12:23:33 -07:00
"github.com/jpillora/backoff"
2018-11-15 10:24:22 -08:00
prefixed "github.com/matterbridge/logrus-prefixed-formatter"
"github.com/mattermost/mattermost-server/model"
2018-11-15 10:24:22 -08:00
log "github.com/sirupsen/logrus"
2016-07-11 12:23:33 -07:00
)
type Credentials struct {
Login string
Team string
Pass string
Token string
CookieToken bool
2016-07-11 12:23:33 -07:00
Server string
NoTLS bool
SkipTLSVerify bool
}
type Message struct {
Raw *model.WebSocketEvent
2016-07-11 12:23:33 -07:00
Post *model.Post
Team string
Channel string
Username string
Text string
Type string
UserID string
2016-07-11 12:23:33 -07:00
}
//nolint:golint
2016-07-11 12:23:33 -07:00
type Team struct {
Team *model.Team
Id string
Channels []*model.Channel
MoreChannels []*model.Channel
2016-07-11 12:23:33 -07:00
Users map[string]*model.User
}
type MMClient struct {
sync.RWMutex
*Credentials
2017-04-09 14:15:11 -07:00
Team *Team
OtherTeams []*Team
Client *model.Client4
2017-04-09 14:15:11 -07:00
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
OnWsConnect func()
lruCache *lru.Cache
2016-07-11 12:23:33 -07:00
}
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)}
log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true})
mmclient.log = log.WithFields(log.Fields{"prefix": "matterclient"})
mmclient.lruCache, _ = lru.New(500)
2016-07-11 12:23:33 -07:00
return mmclient
}
func (m *MMClient) SetDebugLog() {
log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true, FullTimestamp: false, ForceFormatting: true})
}
2016-07-11 12:23:33 -07:00
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 {
// check if this is a first connect or a reconnection
firstConnection := true
2017-07-13 15:35:01 -07:00
if m.WsConnected {
firstConnection = false
}
2016-07-11 12:23:33 -07:00
m.WsConnected = false
if m.WsQuit {
return nil
}
b := &backoff.Backoff{
Min: time.Second,
Max: 5 * time.Minute,
Jitter: true,
}
// do initialization setup
if err := m.initClient(firstConnection, b); err != nil {
return err
2017-04-09 14:15:11 -07:00
}
if err := m.doLogin(firstConnection, b); err != nil {
return err
2016-07-11 12:23:33 -07:00
}
if err := m.initUser(); err != nil {
2016-07-11 12:23:33 -07:00
return err
}
if m.Team == nil {
validTeamNames := make([]string, len(m.OtherTeams))
for i, t := range m.OtherTeams {
validTeamNames[i] = t.Team.Name
}
return fmt.Errorf("Team '%s' not found in %v", m.Credentials.Team, validTeamNames)
2016-07-11 12:23:33 -07:00
}
m.wsConnect()
return nil
}
2016-07-11 12:23:33 -07:00
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()
if strings.Contains(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN) {
m.log.Debug("Not invalidating session in logout, credential is a token")
return nil
}
_, resp := m.Client.Logout()
if resp.Error != nil {
return resp.Error
2016-07-11 12:23:33 -07:00
}
return nil
}
func (m *MMClient) WsReceiver() {
for {
var rawMsg json.RawMessage
var err error
if m.WsQuit {
m.log.Debug("exiting WsReceiver")
return
2016-07-11 12:23:33 -07:00
}
2016-09-05 14:08:17 -07:00
if !m.WsConnected {
time.Sleep(time.Millisecond * 100)
continue
}
2016-08-19 13:58:42 -07:00
if _, rawMsg, err = m.WsClient.ReadMessage(); err != nil {
m.log.Error("error:", err)
// reconnect
m.wsConnect()
2016-08-19 13:58:42 -07:00
}
var event model.WebSocketEvent
if err := json.Unmarshal(rawMsg, &event); err == nil && event.IsValid() {
m.log.Debugf("WsReceiver event: %#v", event)
msg := &Message{Raw: &event, Team: m.Credentials.Team}
m.parseMessage(msg)
// check if we didn't empty the message
if msg.Text != "" {
m.MessageChan <- msg
continue
}
// if we have file attached but the message is empty, also send it
if msg.Post != nil {
if msg.Text != "" || len(msg.Post.FileIds) > 0 || msg.Post.Type == "slack_attachment" {
m.MessageChan <- msg
}
}
continue
}
2016-07-11 12:23:33 -07:00
var response model.WebSocketResponse
if err := json.Unmarshal(rawMsg, &response); err == nil && response.IsValid() {
m.log.Debugf("WsReceiver response: %#v", response)
m.parseResponse(response)
continue
}
2016-07-11 12:23:33 -07:00
}
}
func (m *MMClient) StatusLoop() {
retries := 0
backoff := time.Second * 60
if m.OnWsConnect != nil {
m.OnWsConnect()
}
m.log.Debug("StatusLoop:", m.OnWsConnect != nil)
for {
if m.WsQuit {
return
}
if m.WsConnected {
m.checkAlive()
select {
case <-m.WsPingChan:
m.log.Debug("WS PONG received")
backoff = time.Second * 60
case <-time.After(time.Second * 5):
if retries > 3 {
2017-11-16 11:19:52 -08:00
m.log.Debug("StatusLoop() timeout")
m.Logout()
m.WsQuit = false
2017-11-16 11:19:52 -08:00
err := m.Login()
if err != nil {
log.Errorf("Login failed: %#v", err)
break
}
if m.OnWsConnect != nil {
m.OnWsConnect()
}
go m.WsReceiver()
} else {
retries++
backoff = time.Second * 5
}
}
}
time.Sleep(backoff)
}
}