summaryrefslogtreecommitdiffstats
path: root/matterclient
diff options
context:
space:
mode:
authorWim <wim@42.be>2023-08-05 20:43:19 +0200
committerGitHub <noreply@github.com>2023-08-05 20:43:19 +0200
commit56e7bd01ca09ad52b0c4f48f146a20a4f1b78696 (patch)
treeb1355645342667209263cbd355dc0b4254f1e8fe /matterclient
parent9459495484d6e06a3d46de64fccd8d06f7ccc72c (diff)
downloadmatterbridge-msglm-master.tar.gz
matterbridge-msglm-master.tar.bz2
matterbridge-msglm-master.zip
Update dependencies and remove old matterclient lib (#2067)HEADmaster
Diffstat (limited to 'matterclient')
-rw-r--r--matterclient/README.md1
-rw-r--r--matterclient/channels.go226
-rw-r--r--matterclient/helpers.go297
-rw-r--r--matterclient/matterclient.go294
-rw-r--r--matterclient/messages.go207
-rw-r--r--matterclient/users.go165
6 files changed, 1 insertions, 1189 deletions
diff --git a/matterclient/README.md b/matterclient/README.md
new file mode 100644
index 00000000..0472dc73
--- /dev/null
+++ b/matterclient/README.md
@@ -0,0 +1 @@
+Find matterclient on https://github.com/matterbridge/matterclient
diff --git a/matterclient/channels.go b/matterclient/channels.go
deleted file mode 100644
index 13d3277d..00000000
--- a/matterclient/channels.go
+++ /dev/null
@@ -1,226 +0,0 @@
-package matterclient
-
-import (
- "errors"
- "strings"
-
- "github.com/mattermost/mattermost-server/v5/model"
-)
-
-// 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...)
- for _, t := range m.OtherTeams {
- if t.Id != m.Team.Id {
- channels = append(channels, t.Channels...)
- }
- }
- return channels
-}
-
-func (m *MMClient) GetChannelHeader(channelId string) string { //nolint:golint
- m.RLock()
- defer m.RUnlock()
- for _, t := range m.OtherTeams {
- for _, channel := range append(t.Channels, t.MoreChannels...) {
- if channel.Id == channelId {
- return channel.Header
- }
-
- }
- }
- return ""
-}
-
-func getNormalisedName(channel *model.Channel) string {
- if channel.Type == model.CHANNEL_GROUP {
- // (deprecated in favor of ReplaceAll in go 1.12)
- res := strings.Replace(channel.DisplayName, ", ", "-", -1) //nolint: gocritic
- res = strings.Replace(res, " ", "_", -1) //nolint: gocritic
- return res
- }
- return channel.Name
-}
-
-func (m *MMClient) GetChannelId(name string, teamId string) string { //nolint:golint
- m.RLock()
- defer m.RUnlock()
- if teamId != "" {
- return m.getChannelIdTeam(name, teamId)
- }
-
- for _, t := range m.OtherTeams {
- for _, channel := range append(t.Channels, t.MoreChannels...) {
- if getNormalisedName(channel) == name {
- return channel.Id
- }
- }
- }
- return ""
-}
-
-func (m *MMClient) getChannelIdTeam(name string, teamId string) string { //nolint:golint
- for _, t := range m.OtherTeams {
- if t.Id == teamId {
- for _, channel := range append(t.Channels, t.MoreChannels...) {
- if getNormalisedName(channel) == name {
- return channel.Id
- }
- }
- }
- }
- return ""
-}
-
-func (m *MMClient) GetChannelName(channelId string) string { //nolint:golint
- m.RLock()
- defer m.RUnlock()
- for _, t := range m.OtherTeams {
- if t == nil {
- continue
- }
- for _, channel := range append(t.Channels, t.MoreChannels...) {
- if channel.Id == channelId {
- return getNormalisedName(channel)
- }
- }
- }
- return ""
-}
-
-func (m *MMClient) GetChannelTeamId(id string) string { //nolint:golint
- m.RLock()
- defer m.RUnlock()
- for _, t := range append(m.OtherTeams, m.Team) {
- for _, channel := range append(t.Channels, t.MoreChannels...) {
- if channel.Id == id {
- return channel.TeamId
- }
- }
- }
- return ""
-}
-
-func (m *MMClient) GetLastViewedAt(channelId string) int64 { //nolint:golint
- m.RLock()
- defer m.RUnlock()
- res, resp := m.Client.GetChannelMember(channelId, m.User.Id, "")
- if resp.Error != nil {
- return model.GetMillis()
- }
- return res.LastViewedAt
-}
-
-// 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...)
- }
- return channels
-}
-
-// GetTeamFromChannel returns teamId belonging to channel (DM channels have no teamId).
-func (m *MMClient) GetTeamFromChannel(channelId string) string { //nolint:golint
- m.RLock()
- defer m.RUnlock()
- var channels []*model.Channel
- for _, t := range m.OtherTeams {
- channels = append(channels, t.Channels...)
- if t.MoreChannels != nil {
- channels = append(channels, t.MoreChannels...)
- }
- for _, c := range channels {
- if c.Id == channelId {
- if c.Type == model.CHANNEL_GROUP {
- return "G"
- }
- return t.Id
- }
- }
- channels = nil
- }
- return ""
-}
-
-func (m *MMClient) JoinChannel(channelId string) error { //nolint:golint
- m.RLock()
- defer m.RUnlock()
- for _, c := range m.Team.Channels {
- if c.Id == channelId {
- m.logger.Debug("Not joining ", channelId, " already joined.")
- return nil
- }
- }
- m.logger.Debug("Joining ", channelId)
- _, resp := m.Client.AddChannelMember(channelId, m.User.Id)
- if resp.Error != nil {
- return resp.Error
- }
- return nil
-}
-
-func (m *MMClient) UpdateChannelsTeam(teamID string) error {
- mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, false, "")
- if resp.Error != nil {
- return errors.New(resp.Error.DetailedError)
- }
- for idx, t := range m.OtherTeams {
- if t.Id == teamID {
- m.Lock()
- m.OtherTeams[idx].Channels = mmchannels
- m.Unlock()
- }
- }
-
- mmchannels, resp = m.Client.GetPublicChannelsForTeam(teamID, 0, 5000, "")
- if resp.Error != nil {
- return errors.New(resp.Error.DetailedError)
- }
- for idx, t := range m.OtherTeams {
- if t.Id == teamID {
- m.Lock()
- m.OtherTeams[idx].MoreChannels = mmchannels
- m.Unlock()
- }
- }
- return nil
-}
-
-func (m *MMClient) UpdateChannels() error {
- if err := m.UpdateChannelsTeam(m.Team.Id); err != nil {
- return err
- }
- for _, t := range m.OtherTeams {
- if err := m.UpdateChannelsTeam(t.Id); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (m *MMClient) UpdateChannelHeader(channelId string, header string) { //nolint:golint
- channel := &model.Channel{Id: channelId, Header: header}
- m.logger.Debugf("updating channelheader %#v, %#v", channelId, header)
- _, resp := m.Client.UpdateChannel(channel)
- if resp.Error != nil {
- m.logger.Error(resp.Error)
- }
-}
-
-func (m *MMClient) UpdateLastViewed(channelId string) error { //nolint:golint
- m.logger.Debugf("posting lastview %#v", channelId)
- view := &model.ChannelView{ChannelId: channelId}
- _, resp := m.Client.ViewChannel(m.User.Id, view)
- if resp.Error != nil {
- m.logger.Errorf("ChannelView update for %s failed: %s", channelId, resp.Error)
- return resp.Error
- }
- return nil
-}
diff --git a/matterclient/helpers.go b/matterclient/helpers.go
deleted file mode 100644
index 99743af0..00000000
--- a/matterclient/helpers.go
+++ /dev/null
@@ -1,297 +0,0 @@
-package matterclient
-
-import (
- "crypto/md5" //nolint:gosec
- "crypto/tls"
- "errors"
- "fmt"
- "net/http"
- "net/http/cookiejar"
- "net/url"
- "strings"
- "time"
-
- "github.com/gorilla/websocket"
- "github.com/jpillora/backoff"
- "github.com/mattermost/mattermost-server/v5/model"
-)
-
-func (m *MMClient) doLogin(firstConnection bool, b *backoff.Backoff) error {
- var resp *model.Response
- var appErr *model.AppError
- var logmsg = "trying login"
- var err error
- for {
- m.logger.Debugf("%s %s %s %s", logmsg, m.Credentials.Team, m.Credentials.Login, m.Credentials.Server)
- if m.Credentials.Token != "" {
- resp, err = m.doLoginToken()
- if err != nil {
- return err
- }
- } else {
- m.User, resp = m.Client.Login(m.Credentials.Login, m.Credentials.Pass)
- }
- appErr = resp.Error
- if appErr != nil {
- d := b.Duration()
- m.logger.Debug(appErr.DetailedError)
- if firstConnection {
- if appErr.Message == "" {
- return errors.New(appErr.DetailedError)
- }
- return errors.New(appErr.Message)
- }
- m.logger.Debugf("LOGIN: %s, reconnecting in %s", appErr, d)
- time.Sleep(d)
- logmsg = "retrying login"
- continue
- }
- break
- }
- // reset timer
- b.Reset()
- return nil
-}
-
-func (m *MMClient) doLoginToken() (*model.Response, error) {
- var resp *model.Response
- var logmsg = "trying login"
- m.Client.AuthType = model.HEADER_BEARER
- m.Client.AuthToken = m.Credentials.Token
- if m.Credentials.CookieToken {
- m.logger.Debugf(logmsg + " with cookie (MMAUTH) token")
- m.Client.HttpClient.Jar = m.createCookieJar(m.Credentials.Token)
- } else {
- m.logger.Debugf(logmsg + " with personal token")
- }
- m.User, resp = m.Client.GetMe("")
- if resp.Error != nil {
- return resp, resp.Error
- }
- if m.User == nil {
- m.logger.Errorf("LOGIN TOKEN: %s is invalid", m.Credentials.Pass)
- return resp, errors.New("invalid token")
- }
- return resp, nil
-}
-
-func (m *MMClient) handleLoginToken() error {
- switch {
- case strings.Contains(m.Credentials.Pass, 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.Credentials.Token = token[1]
- m.Credentials.CookieToken = true
- case strings.Contains(m.Credentials.Pass, "token="):
- token := strings.Split(m.Credentials.Pass, "token=")
- if len(token) != 2 {
- return errors.New("incorrect personal token. valid input is token=yourtoken")
- }
- m.Credentials.Token = token[1]
- }
- return nil
-}
-
-func (m *MMClient) initClient(firstConnection bool, b *backoff.Backoff) error {
- uriScheme := "https://"
- if m.NoTLS {
- uriScheme = "http://"
- }
- // login to mattermost
- m.Client = model.NewAPIv4Client(uriScheme + m.Credentials.Server)
- m.Client.HttpClient.Transport = &http.Transport{
- TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}, //nolint:gosec
- Proxy: http.ProxyFromEnvironment,
- }
- m.Client.HttpClient.Timeout = time.Second * 10
-
- // handle MMAUTHTOKEN and personal token
- if err := m.handleLoginToken(); err != nil {
- return err
- }
-
- // check if server alive, retry until
- if err := m.serverAlive(firstConnection, b); err != nil {
- return err
- }
-
- return nil
-}
-
-// initialize user and teams
-func (m *MMClient) initUser() error {
- m.Lock()
- defer m.Unlock()
- // we only load all team data on initial login.
- // all other updates are for channels from our (primary) team only.
- //m.logger.Debug("initUser(): loading all team data")
- teams, resp := m.Client.GetTeamsForUser(m.User.Id, "")
- if resp.Error != nil {
- return resp.Error
- }
- for _, team := range teams {
- idx := 0
- max := 200
- usermap := make(map[string]*model.User)
- mmusers, resp := m.Client.GetUsersInTeam(team.Id, idx, max, "")
- if resp.Error != nil {
- return errors.New(resp.Error.DetailedError)
- }
- for len(mmusers) > 0 {
- for _, user := range mmusers {
- usermap[user.Id] = user
- }
- mmusers, resp = m.Client.GetUsersInTeam(team.Id, idx, max, "")
- if resp.Error != nil {
- return errors.New(resp.Error.DetailedError)
- }
- idx++
- time.Sleep(time.Millisecond * 200)
- }
- m.logger.Infof("found %d users in team %s", len(usermap), team.Name)
-
- t := &Team{Team: team, Users: usermap, Id: team.Id}
-
- mmchannels, resp := m.Client.GetChannelsForTeamForUser(team.Id, m.User.Id, false, "")
- if resp.Error != nil {
- return resp.Error
- }
- t.Channels = mmchannels
- mmchannels, resp = m.Client.GetPublicChannelsForTeam(team.Id, 0, 5000, "")
- if resp.Error != nil {
- return resp.Error
- }
- t.MoreChannels = mmchannels
- m.OtherTeams = append(m.OtherTeams, t)
- if team.Name == m.Credentials.Team {
- m.Team = t
- m.logger.Debugf("initUser(): found our team %s (id: %s)", team.Name, team.Id)
- }
- // add all users
- for k, v := range t.Users {
- m.Users[k] = v
- }
- }
- return nil
-}
-
-func (m *MMClient) serverAlive(firstConnection bool, b *backoff.Backoff) error {
- defer b.Reset()
- for {
- d := b.Duration()
- // bogus call to get the serverversion
- _, resp := m.Client.Logout()
- if resp.Error != nil {
- return fmt.Errorf("%#v", resp.Error.Error())
- }
- if firstConnection && !m.SkipVersionCheck && !supportedVersion(resp.ServerVersion) {
- return fmt.Errorf("unsupported mattermost version: %s", resp.ServerVersion)
- }
- if !m.SkipVersionCheck {
- m.ServerVersion = resp.ServerVersion
- if m.ServerVersion == "" {
- m.logger.Debugf("Server not up yet, reconnecting in %s", d)
- time.Sleep(d)
- } else {
- m.logger.Infof("Found version %s", m.ServerVersion)
- return nil
- }
- } else {
- return nil
- }
- }
-}
-
-func (m *MMClient) wsConnect() {
- b := &backoff.Backoff{
- Min: time.Second,
- Max: 5 * time.Minute,
- Jitter: true,
- }
-
- m.WsConnected = false
- wsScheme := "wss://"
- if m.NoTLS {
- wsScheme = "ws://"
- }
-
- // setup websocket connection
- wsurl := wsScheme + m.Credentials.Server + model.API_URL_SUFFIX_V4 + "/websocket"
- header := http.Header{}
- header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
-
- m.logger.Debugf("WsClient: making connection: %s", wsurl)
- for {
- wsDialer := &websocket.Dialer{
- TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}, //nolint:gosec
- Proxy: http.ProxyFromEnvironment,
- }
- var err error
- m.WsClient, _, err = wsDialer.Dial(wsurl, header)
- if err != nil {
- d := b.Duration()
- m.logger.Debugf("WSS: %s, reconnecting in %s", err, d)
- time.Sleep(d)
- continue
- }
- break
- }
-
- m.logger.Debug("WsClient: connected")
- m.WsSequence = 1
- m.WsPingChan = make(chan *model.WebSocketResponse)
- // only start to parse WS messages when login is completely done
- m.WsConnected = true
-}
-
-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
-}
-
-func (m *MMClient) checkAlive() error {
- // check if session still is valid
- _, resp := m.Client.GetMe("")
- if resp.Error != nil {
- return resp.Error
- }
- m.logger.Debug("WS PING")
- return m.sendWSRequest("ping", nil)
-}
-
-func (m *MMClient) sendWSRequest(action string, data map[string]interface{}) error {
- req := &model.WebSocketRequest{}
- req.Seq = m.WsSequence
- req.Action = action
- req.Data = data
- m.WsSequence++
- m.logger.Debugf("sendWsRequest %#v", req)
- return m.WsClient.WriteJSON(req)
-}
-
-func supportedVersion(version string) bool {
- if strings.HasPrefix(version, "3.8.0") ||
- strings.HasPrefix(version, "3.9.0") ||
- strings.HasPrefix(version, "3.10.0") ||
- strings.HasPrefix(version, "4.") ||
- strings.HasPrefix(version, "5.") {
- return true
- }
- return false
-}
-
-func digestString(s string) string {
- return fmt.Sprintf("%x", md5.Sum([]byte(s))) //nolint:gosec
-}
diff --git a/matterclient/matterclient.go b/matterclient/matterclient.go
deleted file mode 100644
index ffe88aa4..00000000
--- a/matterclient/matterclient.go
+++ /dev/null
@@ -1,294 +0,0 @@
-package matterclient
-
-import (
- "encoding/json"
- "fmt"
- "strings"
- "sync"
- "time"
-
- "github.com/gorilla/websocket"
- lru "github.com/hashicorp/golang-lru"
- "github.com/jpillora/backoff"
- prefixed "github.com/matterbridge/logrus-prefixed-formatter"
- "github.com/mattermost/mattermost-server/v5/model"
- "github.com/sirupsen/logrus"
-)
-
-type Credentials struct {
- Login string
- Team string
- Pass string
- Token string
- CookieToken bool
- Server string
- NoTLS bool
- SkipTLSVerify bool
- SkipVersionCheck bool
-}
-
-type Message struct {
- Raw *model.WebSocketEvent
- Post *model.Post
- Team string
- Channel string
- Username string
- Text string
- Type string
- UserID string
-}
-
-//nolint:golint
-type Team struct {
- Team *model.Team
- Id string
- Channels []*model.Channel
- MoreChannels []*model.Channel
- Users map[string]*model.User
-}
-
-type MMClient struct {
- sync.RWMutex
- *Credentials
-
- Team *Team
- OtherTeams []*Team
- Client *model.Client4
- User *model.User
- Users map[string]*model.User
- MessageChan chan *Message
- WsClient *websocket.Conn
- WsQuit bool
- WsAway bool
- WsConnected bool
- WsSequence int64
- WsPingChan chan *model.WebSocketResponse
- ServerVersion string
- OnWsConnect func()
-
- logger *logrus.Entry
- rootLogger *logrus.Logger
- lruCache *lru.Cache
- allevents bool
-}
-
-// New will instantiate a new Matterclient with the specified login details without connecting.
-func New(login string, pass string, team string, server string) *MMClient {
- rootLogger := logrus.New()
- rootLogger.SetFormatter(&prefixed.TextFormatter{
- PrefixPadding: 13,
- DisableColors: true,
- })
-
- cred := &Credentials{
- Login: login,
- Pass: pass,
- Team: team,
- Server: server,
- }
-
- cache, _ := lru.New(500)
- return &MMClient{
- Credentials: cred,
- MessageChan: make(chan *Message, 100),
- Users: make(map[string]*model.User),
- rootLogger: rootLogger,
- lruCache: cache,
- logger: rootLogger.WithFields(logrus.Fields{"prefix": "matterclient"}),
- }
-}
-
-// SetDebugLog activates debugging logging on all Matterclient log output.
-func (m *MMClient) SetDebugLog() {
- m.rootLogger.SetFormatter(&prefixed.TextFormatter{
- PrefixPadding: 13,
- DisableColors: true,
- FullTimestamp: false,
- ForceFormatting: true,
- })
-}
-
-// SetLogLevel tries to parse the specified level and if successful sets
-// the log level accordingly. Accepted levels are: 'debug', 'info', 'warn',
-// 'error', 'fatal' and 'panic'.
-func (m *MMClient) SetLogLevel(level string) {
- l, err := logrus.ParseLevel(level)
- if err != nil {
- m.logger.Warnf("Failed to parse specified log-level '%s': %#v", level, err)
- } else {
- m.rootLogger.SetLevel(l)
- }
-}
-
-func (m *MMClient) EnableAllEvents() {
- m.allevents = true
-}
-
-// Login tries to connect the client with the loging details with which it was initialized.
-func (m *MMClient) Login() error {
- // check if this is a first connect or a reconnection
- firstConnection := true
- if m.WsConnected {
- firstConnection = false
- }
- 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
- }
-
- if err := m.doLogin(firstConnection, b); err != nil {
- return err
- }
-
- if err := m.initUser(); err != nil {
- 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)
- }
-
- m.wsConnect()
-
- return nil
-}
-
-// Logout disconnects the client from the chat server.
-func (m *MMClient) Logout() error {
- m.logger.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.logger.Debug("Not invalidating session in logout, credential is a token")
- return nil
- }
- _, resp := m.Client.Logout()
- if resp.Error != nil {
- return resp.Error
- }
- return nil
-}
-
-// WsReceiver implements the core loop that manages the connection to the chat server. In
-// case of a disconnect it will try to reconnect. A call to this method is blocking until
-// the 'WsQuite' field of the MMClient object is set to 'true'.
-func (m *MMClient) WsReceiver() {
- for {
- var rawMsg json.RawMessage
- var err error
-
- if m.WsQuit {
- m.logger.Debug("exiting WsReceiver")
- return
- }
-
- if !m.WsConnected {
- time.Sleep(time.Millisecond * 100)
- continue
- }
-
- if _, rawMsg, err = m.WsClient.ReadMessage(); err != nil {
- m.logger.Error("error:", err)
- // reconnect
- m.wsConnect()
- }
-
- var event model.WebSocketEvent
- if err := json.Unmarshal(rawMsg, &event); err == nil && event.IsValid() {
- m.logger.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
- }
- }
- if m.allevents {
- m.MessageChan <- msg
- continue
- }
- switch msg.Raw.Event {
- case model.WEBSOCKET_EVENT_USER_ADDED,
- model.WEBSOCKET_EVENT_USER_REMOVED,
- model.WEBSOCKET_EVENT_CHANNEL_CREATED,
- model.WEBSOCKET_EVENT_CHANNEL_DELETED:
- m.MessageChan <- msg
- continue
- }
- }
-
- var response model.WebSocketResponse
- if err := json.Unmarshal(rawMsg, &response); err == nil && response.IsValid() {
- m.logger.Debugf("WsReceiver response: %#v", response)
- m.parseResponse(response)
- }
- }
-}
-
-// StatusLoop implements a ping-cycle that ensures that the connection to the chat servers
-// remains alive. In case of a disconnect it will try to reconnect. A call to this method
-// is blocking until the 'WsQuite' field of the MMClient object is set to 'true'.
-func (m *MMClient) StatusLoop() {
- retries := 0
- backoff := time.Second * 60
- if m.OnWsConnect != nil {
- m.OnWsConnect()
- }
- m.logger.Debug("StatusLoop:", m.OnWsConnect != nil)
- for {
- if m.WsQuit {
- return
- }
- if m.WsConnected {
- if err := m.checkAlive(); err != nil {
- m.logger.Errorf("Connection is not alive: %#v", err)
- }
- select {
- case <-m.WsPingChan:
- m.logger.Debug("WS PONG received")
- backoff = time.Second * 60
- case <-time.After(time.Second * 5):
- if retries > 3 {
- m.logger.Debug("StatusLoop() timeout")
- m.Logout()
- m.WsQuit = false
- err := m.Login()
- if err != nil {
- m.logger.Errorf("Login failed: %#v", err)
- break
- }
- if m.OnWsConnect != nil {
- m.OnWsConnect()
- }
- go m.WsReceiver()
- } else {
- retries++
- backoff = time.Second * 5
- }
- }
- }
- time.Sleep(backoff)
- }
-}
diff --git a/matterclient/messages.go b/matterclient/messages.go
deleted file mode 100644
index 81c3ecbe..00000000
--- a/matterclient/messages.go
+++ /dev/null
@@ -1,207 +0,0 @@
-package matterclient
-
-import (
- "strings"
-
- "github.com/mattermost/mattermost-server/v5/model"
-)
-
-func (m *MMClient) parseActionPost(rmsg *Message) {
- // add post to cache, if it already exists don't relay this again.
- // this should fix reposts
- if ok, _ := m.lruCache.ContainsOrAdd(digestString(rmsg.Raw.Data["post"].(string)), true); ok && rmsg.Raw.Event != model.WEBSOCKET_EVENT_POST_DELETED {
- m.logger.Debugf("message %#v in cache, not processing again", rmsg.Raw.Data["post"].(string))
- rmsg.Text = ""
- return
- }
- data := model.PostFromJson(strings.NewReader(rmsg.Raw.Data["post"].(string)))
- // we don't have the user, refresh the userlist
- if m.GetUser(data.UserId) == nil {
- m.logger.Infof("User '%v' is not known, ignoring message '%#v'",
- data.UserId, data)
- return
- }
- rmsg.Username = m.GetUserName(data.UserId)
- rmsg.Channel = m.GetChannelName(data.ChannelId)
- rmsg.UserID = data.UserId
- 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
- teamid = m.GetChannelTeamId(data.ChannelId)
- 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
- }
- rmsg.Text = data.Message
- rmsg.Post = data
-}
-
-func (m *MMClient) parseMessage(rmsg *Message) {
- switch rmsg.Raw.Event {
- case model.WEBSOCKET_EVENT_POSTED, model.WEBSOCKET_EVENT_POST_EDITED, model.WEBSOCKET_EVENT_POST_DELETED:
- m.parseActionPost(rmsg)
- case "user_updated":
- user := rmsg.Raw.Data["user"].(map[string]interface{})
- if _, ok := user["id"].(string); ok {
- m.UpdateUser(user["id"].(string))
- }
- case "group_added":
- if err := m.UpdateChannels(); err != nil {
- m.logger.Errorf("failed to update channels: %#v", err)
- }
- /*
- case model.ACTION_USER_REMOVED:
- m.handleWsActionUserRemoved(&rmsg)
- case model.ACTION_USER_ADDED:
- m.handleWsActionUserAdded(&rmsg)
- */
- }
-}
-
-func (m *MMClient) parseResponse(rmsg model.WebSocketResponse) {
- if rmsg.Data != nil {
- // ping reply
- if rmsg.Data["text"].(string) == "pong" {
- m.WsPingChan <- &rmsg
- }
- }
-}
-
-func (m *MMClient) DeleteMessage(postId string) error { //nolint:golint
- _, resp := m.Client.DeletePost(postId)
- if resp.Error != nil {
- return resp.Error
- }
- return nil
-}
-
-func (m *MMClient) EditMessage(postId string, text string) (string, error) { //nolint:golint
- post := &model.Post{Message: text, Id: postId}
- res, resp := m.Client.UpdatePost(postId, post)
- if resp.Error != nil {
- return "", resp.Error
- }
- return res.Id, nil
-}
-
-func (m *MMClient) GetFileLinks(filenames []string) []string {
- uriScheme := "https://"
- if m.NoTLS {
- uriScheme = "http://"
- }
-
- var output []string
- for _, f := range filenames {
- res, resp := m.Client.GetFileLink(f)
- if resp.Error != nil {
- // public links is probably disabled, create the link ourselves
- output = append(output, uriScheme+m.Credentials.Server+model.API_URL_SUFFIX_V4+"/files/"+f)
- continue
- }
- output = append(output, res)
- }
- return output
-}
-
-func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList { //nolint:golint
- res, resp := m.Client.GetPostsForChannel(channelId, 0, limit, "", true)
- if resp.Error != nil {
- return nil
- }
- return res
-}
-
-func (m *MMClient) GetPostsSince(channelId string, time int64) *model.PostList { //nolint:golint
- res, resp := m.Client.GetPostsSince(channelId, time, true)
- if resp.Error != nil {
- return nil
- }
- return res
-}
-
-func (m *MMClient) GetPublicLink(filename string) string {
- res, resp := m.Client.GetFileLink(filename)
- if resp.Error != nil {
- return ""
- }
- return res
-}
-
-func (m *MMClient) GetPublicLinks(filenames []string) []string {
- var output []string
- for _, f := range filenames {
- res, resp := m.Client.GetFileLink(f)
- if resp.Error != nil {
- continue
- }
- output = append(output, res)
- }
- return output
-}
-
-func (m *MMClient) PostMessage(channelId string, text string, rootId string) (string, error) { //nolint:golint
- post := &model.Post{ChannelId: channelId, Message: text, RootId: rootId}
- res, resp := m.Client.CreatePost(post)
- if resp.Error != nil {
- return "", resp.Error
- }
- return res.Id, nil
-}
-
-func (m *MMClient) PostMessageWithFiles(channelId string, text string, rootId string, fileIds []string) (string, error) { //nolint:golint
- post := &model.Post{ChannelId: channelId, Message: text, RootId: rootId, FileIds: fileIds}
- res, resp := m.Client.CreatePost(post)
- if resp.Error != nil {
- return "", resp.Error
- }
- return res.Id, nil
-}
-
-func (m *MMClient) SearchPosts(query string) *model.PostList {
- res, resp := m.Client.SearchPosts(m.Team.Id, query, false)
- if resp.Error != nil {
- return nil
- }
- return res
-}
-
-// SendDirectMessage sends a direct message to specified user
-func (m *MMClient) SendDirectMessage(toUserId string, msg string, rootId string) { //nolint:golint
- m.SendDirectMessageProps(toUserId, msg, rootId, nil)
-}
-
-func (m *MMClient) SendDirectMessageProps(toUserId string, msg string, rootId string, props map[string]interface{}) { //nolint:golint
- m.logger.Debugf("SendDirectMessage to %s, msg %s", toUserId, msg)
- // create DM channel (only happens on first message)
- _, resp := m.Client.CreateDirectChannel(m.User.Id, toUserId)
- if resp.Error != nil {
- m.logger.Debugf("SendDirectMessage to %#v failed: %s", toUserId, resp.Error)
- return
- }
- channelName := model.GetDMNameFromIds(toUserId, m.User.Id)
-
- // update our channels
- if err := m.UpdateChannels(); err != nil {
- m.logger.Errorf("failed to update channels: %#v", err)
- }
-
- // build & send the message
- msg = strings.Replace(msg, "\r", "", -1)
- post := &model.Post{ChannelId: m.GetChannelId(channelName, m.Team.Id), Message: msg, RootId: rootId, Props: props}
- m.Client.CreatePost(post)
-}
-
-func (m *MMClient) UploadFile(data []byte, channelId string, filename string) (string, error) { //nolint:golint
- f, resp := m.Client.UploadFile(data, channelId, filename)
- if resp.Error != nil {
- return "", resp.Error
- }
- return f.FileInfos[0].Id, nil
-}
diff --git a/matterclient/users.go b/matterclient/users.go
deleted file mode 100644
index 42c4162c..00000000
--- a/matterclient/users.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package matterclient
-
-import (
- "errors"
- "time"
-
- "github.com/mattermost/mattermost-server/v5/model"
-)
-
-func (m *MMClient) GetNickName(userId string) string { //nolint:golint
- user := m.GetUser(userId)
- if user != nil {
- return user.Nickname
- }
- return ""
-}
-
-func (m *MMClient) GetStatus(userId string) string { //nolint:golint
- res, resp := m.Client.GetUserStatus(userId, "")
- if resp.Error != nil {
- return ""
- }
- if res.Status == model.STATUS_AWAY {
- return "away"
- }
- if res.Status == model.STATUS_ONLINE {
- return "online"
- }
- return "offline"
-}
-
-func (m *MMClient) GetStatuses() map[string]string {
- var ids []string
- statuses := make(map[string]string)
- for id := range m.Users {
- ids = append(ids, id)
- }
- res, resp := m.Client.GetUsersStatusesByIds(ids)
- if resp.Error != nil {
- return statuses
- }
- for _, status := range res {
- statuses[status.UserId] = "offline"
- if status.Status == model.STATUS_AWAY {
- statuses[status.UserId] = "away"
- }
- if status.Status == model.STATUS_ONLINE {
- statuses[status.UserId] = "online"
- }
- }
- return statuses
-}
-
-func (m *MMClient) GetTeamId() string { //nolint:golint
- return m.Team.Id
-}
-
-// GetTeamName returns the name of the specified teamId
-func (m *MMClient) GetTeamName(teamId string) string { //nolint:golint
- m.RLock()
- defer m.RUnlock()
- for _, t := range m.OtherTeams {
- if t.Id == teamId {
- return t.Team.Name
- }
- }
- return ""
-}
-
-func (m *MMClient) GetUser(userId string) *model.User { //nolint:golint
- m.Lock()
- defer m.Unlock()
- _, ok := m.Users[userId]
- if !ok {
- res, resp := m.Client.GetUser(userId, "")
- if resp.Error != nil {
- return nil
- }
- m.Users[userId] = res
- }
- return m.Users[userId]
-}
-
-func (m *MMClient) GetUserName(userId string) string { //nolint:golint
- user := m.GetUser(userId)
- if user != nil {
- return user.Username
- }
- return ""
-}
-
-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) UpdateUsers() error {
- idx := 0
- max := 200
- mmusers, resp := m.Client.GetUsers(idx, max, "")
- if resp.Error != nil {
- return errors.New(resp.Error.DetailedError)
- }
- for len(mmusers) > 0 {
- m.Lock()
- for _, user := range mmusers {
- m.Users[user.Id] = user
- }
- m.Unlock()
- mmusers, resp = m.Client.GetUsers(idx, max, "")
- time.Sleep(time.Millisecond * 300)
- if resp.Error != nil {
- return errors.New(resp.Error.DetailedError)
- }
- idx++
- }
- return nil
-}
-
-func (m *MMClient) UpdateUserNick(nick string) error {
- user := m.User
- user.Nickname = nick
- _, resp := m.Client.UpdateUser(user)
- if resp.Error != nil {
- return resp.Error
- }
- return nil
-}
-
-func (m *MMClient) UsernamesInChannel(channelId string) []string { //nolint:golint
- res, resp := m.Client.GetChannelMembers(channelId, 0, 50000, "")
- if resp.Error != nil {
- m.logger.Errorf("UsernamesInChannel(%s) failed: %s", channelId, resp.Error)
- return []string{}
- }
- allusers := m.GetUsers()
- result := []string{}
- for _, member := range *res {
- result = append(result, allusers[member.UserId].Nickname)
- }
- return result
-}
-
-func (m *MMClient) UpdateStatus(userId string, status string) error { //nolint:golint
- _, resp := m.Client.UpdateUserStatus(userId, &model.Status{Status: status})
- if resp.Error != nil {
- return resp.Error
- }
- return nil
-}
-
-func (m *MMClient) UpdateUser(userId string) { //nolint:golint
- m.Lock()
- defer m.Unlock()
- res, resp := m.Client.GetUser(userId, "")
- if resp.Error != nil {
- return
- }
- m.Users[userId] = res
-}