diff options
author | Wim <wim@42.be> | 2023-08-05 20:43:19 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-05 20:43:19 +0200 |
commit | 56e7bd01ca09ad52b0c4f48f146a20a4f1b78696 (patch) | |
tree | b1355645342667209263cbd355dc0b4254f1e8fe /matterclient | |
parent | 9459495484d6e06a3d46de64fccd8d06f7ccc72c (diff) | |
download | matterbridge-msglm-master.tar.gz matterbridge-msglm-master.tar.bz2 matterbridge-msglm-master.zip |
Diffstat (limited to 'matterclient')
-rw-r--r-- | matterclient/README.md | 1 | ||||
-rw-r--r-- | matterclient/channels.go | 226 | ||||
-rw-r--r-- | matterclient/helpers.go | 297 | ||||
-rw-r--r-- | matterclient/matterclient.go | 294 | ||||
-rw-r--r-- | matterclient/messages.go | 207 | ||||
-rw-r--r-- | matterclient/users.go | 165 |
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 -} |