Update dependencies and remove old matterclient lib (#2067)

This commit is contained in:
Wim 2023-08-05 20:43:19 +02:00 committed by GitHub
parent 9459495484
commit 56e7bd01ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
772 changed files with 139315 additions and 121315 deletions

72
go.mod
View File

@ -6,56 +6,54 @@ require (
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c
github.com/SevereCloud/vksdk/v2 v2.16.0
github.com/bwmarrin/discordgo v0.27.0
github.com/d5/tengo/v2 v2.13.0
github.com/bwmarrin/discordgo v0.27.1
github.com/d5/tengo/v2 v2.16.1
github.com/davecgh/go-spew v1.1.1
github.com/fsnotify/fsnotify v1.6.0
github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c
github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12
github.com/google/gops v0.3.27
github.com/gorilla/schema v1.2.0
github.com/gorilla/websocket v1.5.0
github.com/harmony-development/shibshib v0.0.0-20220101224523-c98059d09cfa
github.com/hashicorp/golang-lru v0.6.0
github.com/jpillora/backoff v1.0.0
github.com/keybase/go-keybase-chat-bot v0.0.0-20221220212439-e48d9abd2c20
github.com/kyokomi/emoji/v2 v2.2.12
github.com/labstack/echo/v4 v4.10.2
github.com/lrstanley/girc v0.0.0-20221222153823-a92667a5c9b4
github.com/labstack/echo/v4 v4.11.1
github.com/lrstanley/girc v0.0.0-20230729130341-dd5853a5f1a6
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696
github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be
github.com/matterbridge/gomatrix v0.0.0-20220411225302-271e5088ea27
github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
github.com/matterbridge/matterclient v0.0.0-20221106190440-8bcf49695e0d
github.com/matterbridge/matterclient v0.0.0-20230329213635-bc6e42a4a84a
github.com/matterbridge/telegram-bot-api/v6 v6.5.0
github.com/mattermost/mattermost-server/v5 v5.39.3
github.com/mattermost/mattermost-server/v6 v6.7.2
github.com/mattn/godown v0.0.1
github.com/mdp/qrterminal v1.0.1
github.com/mitchellh/mapstructure v1.5.0
github.com/nelsonken/gomf v0.0.0-20190423072027-c65cc0469e94
github.com/olahol/melody v1.1.2
github.com/olahol/melody v1.1.4
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
github.com/rs/xid v1.4.0
github.com/rs/xid v1.5.0
github.com/russross/blackfriday v1.6.0
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
github.com/shazow/ssh-chat v1.10.1
github.com/sirupsen/logrus v1.9.0
github.com/slack-go/slack v0.12.1
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.1
github.com/sirupsen/logrus v1.9.3
github.com/slack-go/slack v0.12.2
github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.4
github.com/vincent-petithory/dataurl v1.0.0
github.com/writeas/go-strip-markdown v2.0.1+incompatible
github.com/yaegashi/msgraph.go v0.1.4
github.com/zfjagann/golang-ring v0.0.0-20220330170733-19bcea1b6289
go.mau.fi/whatsmeow v0.0.0-20230306190159-5caded34a872
golang.org/x/image v0.6.0
golang.org/x/oauth2 v0.6.0
golang.org/x/text v0.8.0
go.mau.fi/whatsmeow v0.0.0-20230805111647-405414b9b5c0
golang.org/x/image v0.11.0
golang.org/x/oauth2 v0.11.0
golang.org/x/text v0.12.0
gomod.garykim.dev/nc-talk v0.3.0
google.golang.org/protobuf v1.29.1
google.golang.org/protobuf v1.31.0
layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14
modernc.org/sqlite v1.21.0
modernc.org/sqlite v1.25.0
)
require (
@ -65,17 +63,16 @@ require (
github.com/apex/log v1.9.0 // indirect
github.com/av-elier/go-decimal-to-rational v0.0.0-20191127152832-89e6aad02ecf // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dyatlov/go-opengraph v0.0.0-20210112100619-dae8665a5b09 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gopackage/ddp v0.0.3 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/graph-gophers/graphql-go v1.3.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
@ -86,10 +83,9 @@ require (
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
github.com/mattermost/logr v1.0.13 // indirect
github.com/mattermost/logr/v2 v2.0.15 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/minio/md5-simd v1.1.2 // indirect
@ -102,7 +98,7 @@ require (
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/philhofer/fwd v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@ -113,8 +109,8 @@ require (
github.com/shazow/rateio v0.0.0-20200113175441-4461efc8bdc4 // indirect
github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
@ -123,18 +119,14 @@ require (
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/wiggin77/cfg v1.0.2 // indirect
github.com/wiggin77/merror v1.0.3 // indirect
github.com/wiggin77/srslog v1.0.1 // indirect
go.mau.fi/libsignal v0.1.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/term v0.11.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.6.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
@ -145,9 +137,9 @@ require (
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.22.3 // indirect
modernc.org/libc v1.24.1 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/memory v1.6.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
@ -156,4 +148,4 @@ require (
//replace github.com/matrix-org/gomatrix => github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419
go 1.18
go 1.19

363
go.sum

File diff suppressed because it is too large Load Diff

1
matterclient/README.md Normal file
View File

@ -0,0 +1 @@
Find matterclient on https://github.com/matterbridge/matterclient

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -22,7 +22,7 @@ import (
)
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
const VERSION = "0.27.0"
const VERSION = "0.27.1"
// New creates a new Discord session with provided token.
// If the token is for a bot, it must be prefixed with "Bot "

View File

<