diff --git a/cmd/xmpp-client-doctor/main.go b/cmd/xmpp-client-doctor/main.go index ac179e8..eac58c7 100644 --- a/cmd/xmpp-client-doctor/main.go +++ b/cmd/xmpp-client-doctor/main.go @@ -124,7 +124,7 @@ func testXMPPClient(config *Config) error { log.Printf("Using insecure TLS configuration (skipping certificate verification)") } tlsConfig := &tls.Config{ - InsecureSkipVerify: true, + InsecureSkipVerify: true, //nolint:gosec // This is a testing tool for development environments } client = xmpp.NewClientWithTLS( config.Server, @@ -302,7 +302,7 @@ func testMUCOperations(client *xmpp.Client, config *Config) error { } start = time.Now() - _, err = client.SendMessage(messageReq) + _, err = client.SendMessage(&messageReq) if err != nil { return fmt.Errorf("failed to send test message to room %s: %w", config.TestRoom, err) } diff --git a/server/bridge/manager.go b/server/bridge/manager.go index 6f5683c..62da439 100644 --- a/server/bridge/manager.go +++ b/server/bridge/manager.go @@ -5,14 +5,17 @@ import ( "fmt" "sync" + mmModel "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost/server/public/plugin" + "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/config" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" - mmModel "github.com/mattermost/mattermost/server/public/model" - "github.com/mattermost/mattermost/server/public/plugin" ) // BridgeManager manages multiple bridge instances +// +//nolint:revive // BridgeManager is clearer than Manager in this context type BridgeManager struct { bridges map[string]model.Bridge mu sync.RWMutex @@ -26,22 +29,15 @@ type BridgeManager struct { } // NewBridgeManager creates a new bridge manager -func NewBridgeManager(logger logger.Logger, api plugin.API, remoteID string) model.BridgeManager { - if logger == nil { - panic("logger cannot be nil") - } - if api == nil { - panic("plugin API cannot be nil") - } - +func NewBridgeManager(log logger.Logger, api plugin.API, remoteID string) model.BridgeManager { ctx, cancel := context.WithCancel(context.Background()) return &BridgeManager{ bridges: make(map[string]model.Bridge), - logger: logger, + logger: log, api: api, remoteID: remoteID, - messageBus: NewMessageBus(logger), + messageBus: NewMessageBus(log), routingCtx: ctx, routingCancel: cancel, } @@ -229,7 +225,7 @@ func (m *BridgeManager) Shutdown() error { } // OnPluginConfigurationChange propagates configuration changes to all registered bridges -func (m *BridgeManager) OnPluginConfigurationChange(config *config.Configuration) error { +func (m *BridgeManager) OnPluginConfigurationChange(cfg *config.Configuration) error { m.mu.RLock() defer m.mu.RUnlock() @@ -241,7 +237,7 @@ func (m *BridgeManager) OnPluginConfigurationChange(config *config.Configuration var errors []error for name, bridge := range m.bridges { - if err := bridge.UpdateConfiguration(config); err != nil { + if err := bridge.UpdateConfiguration(cfg); err != nil { errors = append(errors, fmt.Errorf("failed to update configuration for bridge '%s': %w", name, err)) m.logger.LogError("Failed to update bridge configuration", "bridge_id", name, "error", err) } else { @@ -258,7 +254,7 @@ func (m *BridgeManager) OnPluginConfigurationChange(config *config.Configuration } // CreateChannelMapping handles the creation of a channel mapping by calling the appropriate bridge -func (m *BridgeManager) CreateChannelMapping(req model.CreateChannelMappingRequest) error { +func (m *BridgeManager) CreateChannelMapping(req *model.CreateChannelMappingRequest) error { // Validate request if err := req.Validate(); err != nil { return fmt.Errorf("invalid mapping request: %w", err) @@ -392,7 +388,7 @@ func (m *BridgeManager) DeleteChannepMapping(req model.DeleteChannelMappingReque } // shareChannel creates a shared channel configuration using the Mattermost API -func (m *BridgeManager) shareChannel(req model.CreateChannelMappingRequest) error { +func (m *BridgeManager) shareChannel(req *model.CreateChannelMappingRequest) error { if m.remoteID == "" { return fmt.Errorf("remote ID not set - plugin not registered for shared channels") } diff --git a/server/bridge/mattermost/bridge.go b/server/bridge/mattermost/bridge.go index cc9d935..fdd1c38 100644 --- a/server/bridge/mattermost/bridge.go +++ b/server/bridge/mattermost/bridge.go @@ -6,12 +6,13 @@ import ( "sync" "sync/atomic" + "github.com/mattermost/mattermost/server/public/plugin" + "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/bridge" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/config" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger" pluginModel "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/store/kvstore" - "github.com/mattermost/mattermost/server/public/plugin" ) const ( @@ -49,12 +50,12 @@ type mattermostBridge struct { } // NewBridge creates a new Mattermost bridge -func NewBridge(log logger.Logger, api plugin.API, kvstore kvstore.KVStore, cfg *config.Configuration, botUserID, bridgeID, remoteID string) pluginModel.Bridge { +func NewBridge(log logger.Logger, api plugin.API, store kvstore.KVStore, cfg *config.Configuration, botUserID, bridgeID, remoteID string) pluginModel.Bridge { ctx, cancel := context.WithCancel(context.Background()) b := &mattermostBridge{ logger: log, api: api, - kvstore: kvstore, + kvstore: store, botUserID: botUserID, bridgeID: bridgeID, remoteID: remoteID, @@ -73,13 +74,6 @@ func NewBridge(log logger.Logger, api plugin.API, kvstore kvstore.KVStore, cfg * return b } -// getConfiguration safely retrieves the current configuration -func (b *mattermostBridge) getConfiguration() *config.Configuration { - b.configMu.RLock() - defer b.configMu.RUnlock() - return b.config -} - // UpdateConfiguration updates the bridge configuration func (b *mattermostBridge) UpdateConfiguration(cfg *config.Configuration) error { // Validate configuration using built-in validation @@ -102,10 +96,10 @@ func (b *mattermostBridge) Start() error { b.logger.LogDebug("Starting Mattermost bridge") b.configMu.RLock() - config := b.config + cfg := b.config b.configMu.RUnlock() - if config == nil { + if cfg == nil { return fmt.Errorf("bridge configuration not set") } @@ -207,11 +201,11 @@ func (b *mattermostBridge) IsConnected() bool { // Ping actively tests the Mattermost API connectivity func (b *mattermostBridge) Ping() error { if !b.connected.Load() { - return fmt.Errorf("Mattermost bridge is not connected") + return fmt.Errorf("mattermost bridge is not connected") } if b.api == nil { - return fmt.Errorf("Mattermost API not initialized") + return fmt.Errorf("mattermost API not initialized") } b.logger.LogDebug("Testing Mattermost bridge connectivity with API ping") @@ -221,7 +215,7 @@ func (b *mattermostBridge) Ping() error { version := b.api.GetServerVersion() if version == "" { b.logger.LogWarn("Mattermost bridge ping returned empty version") - return fmt.Errorf("Mattermost API ping returned empty server version") + return fmt.Errorf("mattermost API ping returned empty server version") } b.logger.LogDebug("Mattermost bridge ping successful", "server_version", version) @@ -313,7 +307,7 @@ func (b *mattermostBridge) DeleteChannelMapping(channelID string) error { // ChannelMappingExists checks if a Mattermost channel exists on the server func (b *mattermostBridge) ChannelMappingExists(roomID string) (bool, error) { if b.api == nil { - return false, fmt.Errorf("Mattermost API not initialized") + return false, fmt.Errorf("mattermost API not initialized") } b.logger.LogDebug("Checking if Mattermost channel exists", "channel_id", roomID) diff --git a/server/bridge/mattermost/message_handler.go b/server/bridge/mattermost/message_handler.go index 5e8b2a8..efaecd5 100644 --- a/server/bridge/mattermost/message_handler.go +++ b/server/bridge/mattermost/message_handler.go @@ -4,9 +4,10 @@ import ( "fmt" "strings" + mmModel "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger" pluginModel "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" - mmModel "github.com/mattermost/mattermost/server/public/model" ) // mattermostMessageHandler handles incoming messages for the Mattermost bridge @@ -59,7 +60,7 @@ func (h *mattermostMessageHandler) GetSupportedMessageTypes() []string { // postMessageToMattermost posts a message to a Mattermost channel func (h *mattermostMessageHandler) postMessageToMattermost(msg *pluginModel.BridgeMessage) error { if h.bridge.api == nil { - return fmt.Errorf("Mattermost API not initialized") + return fmt.Errorf("mattermost API not initialized") } // Get the Mattermost channel ID from the channel mapping using the source bridge name @@ -243,7 +244,7 @@ func (r *mattermostUserResolver) ResolveUser(externalUserID string) (*pluginMode } if user == nil { - return nil, fmt.Errorf("Mattermost user not found: %s", externalUserID) + return nil, fmt.Errorf("mattermost user not found: %s", externalUserID) } return &pluginModel.ExternalUser{ diff --git a/server/bridge/mattermost/user.go b/server/bridge/mattermost/user.go index 8c4e0bd..203169b 100644 --- a/server/bridge/mattermost/user.go +++ b/server/bridge/mattermost/user.go @@ -6,14 +6,17 @@ import ( "sync" "time" + mmModel "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost/server/public/plugin" + "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/config" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" - mmModel "github.com/mattermost/mattermost/server/public/model" - "github.com/mattermost/mattermost/server/public/plugin" ) // MattermostUser represents a Mattermost user that implements the BridgeUser interface +// +//nolint:revive // MattermostUser is clearer than User in this context type MattermostUser struct { // User identity id string @@ -40,7 +43,7 @@ type MattermostUser struct { } // NewMattermostUser creates a new Mattermost user -func NewMattermostUser(id, displayName, username, email string, api plugin.API, cfg *config.Configuration, logger logger.Logger) *MattermostUser { +func NewMattermostUser(id, displayName, username, email string, api plugin.API, cfg *config.Configuration, log logger.Logger) *MattermostUser { ctx, cancel := context.WithCancel(context.Background()) return &MattermostUser{ @@ -53,7 +56,7 @@ func NewMattermostUser(id, displayName, username, email string, api plugin.API, config: cfg, ctx: ctx, cancel: cancel, - logger: logger, + logger: log, } } @@ -69,7 +72,7 @@ func (u *MattermostUser) Validate() error { return fmt.Errorf("configuration cannot be nil") } if u.api == nil { - return fmt.Errorf("Mattermost API cannot be nil") + return fmt.Errorf("mattermost API cannot be nil") } return nil } @@ -192,13 +195,13 @@ func (u *MattermostUser) IsConnected() bool { func (u *MattermostUser) Ping() error { if u.api == nil { - return fmt.Errorf("Mattermost API not initialized for user %s", u.id) + return fmt.Errorf("mattermost API not initialized for user %s", u.id) } // Test API connectivity by getting server version version := u.api.GetServerVersion() if version == "" { - return fmt.Errorf("Mattermost API ping returned empty server version for user %s", u.id) + return fmt.Errorf("mattermost API ping returned empty server version for user %s", u.id) } return nil @@ -207,7 +210,7 @@ func (u *MattermostUser) Ping() error { // CheckChannelExists checks if a Mattermost channel exists func (u *MattermostUser) CheckChannelExists(channelID string) (bool, error) { if u.api == nil { - return false, fmt.Errorf("Mattermost API not initialized for user %s", u.id) + return false, fmt.Errorf("mattermost API not initialized for user %s", u.id) } // Try to get the channel by ID diff --git a/server/bridge/messagebus.go b/server/bridge/messagebus.go index 6def69a..70f5488 100644 --- a/server/bridge/messagebus.go +++ b/server/bridge/messagebus.go @@ -35,7 +35,7 @@ type messageBus struct { } // NewMessageBus creates a new message bus instance -func NewMessageBus(logger logger.Logger) model.MessageBus { +func NewMessageBus(log logger.Logger) model.MessageBus { ctx, cancel := context.WithCancel(context.Background()) return &messageBus{ @@ -43,7 +43,7 @@ func NewMessageBus(logger logger.Logger) model.MessageBus { subscribers: make(map[string]chan *model.DirectionalMessage), ctx: ctx, cancel: cancel, - logger: logger, + logger: log, } } diff --git a/server/bridge/user.go b/server/bridge/user.go index e565eb9..3b3b48a 100644 --- a/server/bridge/user.go +++ b/server/bridge/user.go @@ -21,11 +21,11 @@ type UserManager struct { } // NewUserManager creates a new user manager for a specific bridge type -func NewUserManager(bridgeType string, logger logger.Logger) model.BridgeUserManager { +func NewUserManager(bridgeType string, log logger.Logger) model.BridgeUserManager { ctx, cancel := context.WithCancel(context.Background()) return &UserManager{ bridgeType: bridgeType, - logger: logger, + logger: log, users: make(map[string]model.BridgeUser), ctx: ctx, cancel: cancel, diff --git a/server/bridge/xmpp/bridge.go b/server/bridge/xmpp/bridge.go index 825f60e..e7f76f7 100644 --- a/server/bridge/xmpp/bridge.go +++ b/server/bridge/xmpp/bridge.go @@ -9,15 +9,16 @@ import ( "fmt" + "github.com/mattermost/mattermost/server/public/plugin" + "mellium.im/xmlstream" + "mellium.im/xmpp/stanza" + "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/bridge" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/config" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger" pluginModel "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/store/kvstore" xmppClient "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/xmpp" - "github.com/mattermost/mattermost/server/public/plugin" - "mellium.im/xmlstream" - "mellium.im/xmpp/stanza" ) const ( @@ -55,12 +56,12 @@ type xmppBridge struct { } // NewBridge creates a new XMPP bridge -func NewBridge(log logger.Logger, api plugin.API, kvstore kvstore.KVStore, cfg *config.Configuration, bridgeID, remoteID string) pluginModel.Bridge { +func NewBridge(log logger.Logger, api plugin.API, store kvstore.KVStore, cfg *config.Configuration, bridgeID, remoteID string) pluginModel.Bridge { ctx, cancel := context.WithCancel(context.Background()) b := &xmppBridge{ logger: log, api: api, - kvstore: kvstore, + kvstore: store, ctx: ctx, cancel: cancel, channelMappings: make(map[string]string), @@ -87,7 +88,7 @@ func NewBridge(log logger.Logger, api plugin.API, kvstore kvstore.KVStore, cfg * func (b *xmppBridge) createXMPPClient(cfg *config.Configuration) *xmppClient.Client { // Create TLS config based on certificate verification setting tlsConfig := &tls.Config{ - InsecureSkipVerify: cfg.XMPPInsecureSkipVerify, + InsecureSkipVerify: cfg.XMPPInsecureSkipVerify, //nolint:gosec // Allow insecure TLS for testing environments } return xmppClient.NewClientWithTLS( @@ -154,19 +155,19 @@ func (b *xmppBridge) Start() error { b.logger.LogDebug("Starting Mattermost to XMPP bridge") b.configMu.RLock() - config := b.config + cfg := b.config b.configMu.RUnlock() - if config == nil { + if cfg == nil { return fmt.Errorf("bridge configuration not set") } - if !config.EnableSync { + if !cfg.EnableSync { b.logger.LogInfo("XMPP sync is disabled, bridge will not start") return nil } - b.logger.LogInfo("Starting Mattermost to XMPP bridge", "xmpp_server", config.XMPPServerURL, "username", config.XMPPUsername) + b.logger.LogInfo("Starting Mattermost to XMPP bridge", "xmpp_server", cfg.XMPPServerURL, "username", cfg.XMPPUsername) // Connect to XMPP server if err := b.connectToXMPP(); err != nil { @@ -340,10 +341,10 @@ func (b *xmppBridge) connectionMonitor() { // handleReconnection attempts to reconnect to XMPP and rejoin rooms func (b *xmppBridge) handleReconnection() { b.configMu.RLock() - config := b.config + cfg := b.config b.configMu.RUnlock() - if config == nil || !config.EnableSync { + if cfg == nil || !cfg.EnableSync { return } @@ -357,7 +358,7 @@ func (b *xmppBridge) handleReconnection() { // Retry connection with exponential backoff maxRetries := 3 for i := range maxRetries { - backoff := time.Duration(1< use resource or user - if len(externalUserID) == 0 { + if externalUserID == "" { return "Unknown User" } diff --git a/server/bridge/xmpp/user.go b/server/bridge/xmpp/user.go index fd03afa..c8891ff 100644 --- a/server/bridge/xmpp/user.go +++ b/server/bridge/xmpp/user.go @@ -15,6 +15,8 @@ import ( ) // XMPPUser represents an XMPP user that implements the BridgeUser interface +// +//nolint:revive // XMPPUser is clearer than User in this context type XMPPUser struct { // User identity id string @@ -41,12 +43,12 @@ type XMPPUser struct { } // NewXMPPUser creates a new XMPP user -func NewXMPPUser(id, displayName, jid string, cfg *config.Configuration, logger logger.Logger) *XMPPUser { +func NewXMPPUser(id, displayName, jid string, cfg *config.Configuration, log logger.Logger) *XMPPUser { ctx, cancel := context.WithCancel(context.Background()) // Create TLS config based on certificate verification setting tlsConfig := &tls.Config{ - InsecureSkipVerify: cfg.XMPPInsecureSkipVerify, + InsecureSkipVerify: cfg.XMPPInsecureSkipVerify, //nolint:gosec // Allow insecure TLS for testing environments } // Create XMPP client for this user @@ -57,7 +59,7 @@ func NewXMPPUser(id, displayName, jid string, cfg *config.Configuration, logger cfg.GetXMPPResource(), id, // Use user ID as remote ID tlsConfig, - logger, + log, ) return &XMPPUser{ @@ -69,7 +71,7 @@ func NewXMPPUser(id, displayName, jid string, cfg *config.Configuration, logger config: cfg, ctx: ctx, cancel: cancel, - logger: logger, + logger: log, } } @@ -171,7 +173,7 @@ func (u *XMPPUser) SendMessageToChannel(channelID, message string) error { Message: message, } - _, err := u.client.SendMessage(req) + _, err := u.client.SendMessage(&req) if err != nil { return fmt.Errorf("failed to send message to XMPP room %s: %w", channelID, err) } diff --git a/server/command/command.go b/server/command/command.go index 686eb97..ef099cf 100644 --- a/server/command/command.go +++ b/server/command/command.go @@ -4,10 +4,11 @@ import ( "fmt" "strings" - pluginModel "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/plugin" "github.com/mattermost/mattermost/server/public/pluginapi" + + pluginModel "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" ) type Handler struct { @@ -183,7 +184,7 @@ func (c *Handler) executeMapCommand(args *model.CommandArgs, fields []string) *m TeamID: args.TeamId, } - err = c.bridgeManager.CreateChannelMapping(mappingReq) + err = c.bridgeManager.CreateChannelMapping(&mappingReq) if err != nil { return c.formatMappingError("create", roomJID, err) } @@ -265,11 +266,12 @@ func (c *Handler) executeStatusCommand(args *model.CommandArgs) *model.CommandRe roomJID, err := bridge.GetChannelMapping(channelID) var mappingText string - if err != nil { + switch { + case err != nil: mappingText = fmt.Sprintf("⚠️ Error checking channel mapping: %v", err) - } else if roomJID != "" { + case roomJID != "": mappingText = fmt.Sprintf("🔗 **Current channel mapping:** `%s`", roomJID) - } else { + default: mappingText = "📝 **Current channel:** Not mapped to any XMPP room" } diff --git a/server/configuration.go b/server/configuration.go index b6bad1f..58ab8f8 100644 --- a/server/configuration.go +++ b/server/configuration.go @@ -3,8 +3,9 @@ package main import ( "reflect" - "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/config" "github.com/pkg/errors" + + "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/config" ) // getConfiguration retrieves the active configuration under lock, making it safe to use diff --git a/server/hooks_sharedchannels.go b/server/hooks_sharedchannels.go index 1578de1..5eedd92 100644 --- a/server/hooks_sharedchannels.go +++ b/server/hooks_sharedchannels.go @@ -4,8 +4,9 @@ import ( "fmt" "time" - pluginModel "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" "github.com/mattermost/mattermost/server/public/model" + + pluginModel "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" ) // OnSharedChannelsPing is called to check if the bridge is healthy and ready to process messages @@ -53,7 +54,10 @@ func (p *Plugin) OnSharedChannelsPing(remoteCluster *model.RemoteCluster) bool { // OnSharedChannelsSyncMsg processes sync messages from Mattermost shared channels and routes them to XMPP func (p *Plugin) OnSharedChannelsSyncMsg(msg *model.SyncMsg, rc *model.RemoteCluster) (model.SyncResponse, error) { - p.logger.LogDebug("🚀 OnSharedChannelsSyncMsg called", "remote_id", rc.RemoteId, "channel_id", msg.ChannelId) + var remoteClusterID string + if rc != nil { + remoteClusterID = rc.RemoteId + } config := p.getConfiguration() @@ -65,11 +69,6 @@ func (p *Plugin) OnSharedChannelsSyncMsg(msg *model.SyncMsg, rc *model.RemoteClu ReactionsLastUpdateAt: now, } - var remoteClusterID string - if rc != nil { - remoteClusterID = rc.RemoteId - } - p.logger.LogDebug("OnSharedChannelsSyncMsg called", "remote_cluster_id", remoteClusterID, "channel_id", msg.ChannelId, diff --git a/server/model/bridge.go b/server/model/bridge.go index ce8b728..93dd9e0 100644 --- a/server/model/bridge.go +++ b/server/model/bridge.go @@ -28,7 +28,7 @@ type CreateChannelMappingRequest struct { } // Validate checks if all required fields are present and valid -func (r CreateChannelMappingRequest) Validate() error { +func (r *CreateChannelMappingRequest) Validate() error { if r.ChannelID == "" { return fmt.Errorf("channelID cannot be empty") } @@ -116,7 +116,7 @@ type BridgeManager interface { OnPluginConfigurationChange(config *config.Configuration) error // CreateChannelMapping is called when a channel mapping is created. - CreateChannelMapping(req CreateChannelMappingRequest) error + CreateChannelMapping(req *CreateChannelMappingRequest) error // DeleteChannepMapping is called when a channel mapping is deleted. DeleteChannepMapping(req DeleteChannelMappingRequest) error diff --git a/server/model/message.go b/server/model/message.go index b2640d7..f2cff2d 100644 --- a/server/model/message.go +++ b/server/model/message.go @@ -15,11 +15,11 @@ const ( // BridgeMessage represents a message that can be passed between any bridge types type BridgeMessage struct { // Source information - SourceBridge string // "xmpp", "mattermost", "slack", etc. - SourceChannelID string // Channel ID in source system - SourceUserID string // User ID in source system (JID, user ID, etc.) - SourceUserName string // Display name in source system - SourceRemoteID string // Remote ID of the bridge instance that created this message + SourceBridge string // "xmpp", "mattermost", "slack", etc. + SourceChannelID string // Channel ID in source system + SourceUserID string // User ID in source system (JID, user ID, etc.) + SourceUserName string // Display name in source system + SourceRemoteID string // Remote ID of the bridge instance that created this message // Message content (standardized on Markdown) Content string // Markdown formatted message content diff --git a/server/model/strings.go b/server/model/strings.go index 1e559d9..07ecadc 100644 --- a/server/model/strings.go +++ b/server/model/strings.go @@ -22,12 +22,12 @@ func SanitizeShareName(name string) string { } // Ensure it starts with alphanumeric - for len(result) > 0 && (result[0] == '-' || result[0] == '_') { + for result != "" && (result[0] == '-' || result[0] == '_') { result = result[1:] } // Ensure it ends with alphanumeric - for len(result) > 0 && (result[len(result)-1] == '-' || result[len(result)-1] == '_') { + for result != "" && (result[len(result)-1] == '-' || result[len(result)-1] == '_') { result = result[:len(result)-1] } diff --git a/server/plugin.go b/server/plugin.go index 0b91577..7c4299d 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -6,6 +6,12 @@ import ( "sync" "time" + "github.com/mattermost/mattermost/server/public/model" + "github.com/mattermost/mattermost/server/public/plugin" + "github.com/mattermost/mattermost/server/public/pluginapi" + "github.com/mattermost/mattermost/server/public/pluginapi/cluster" + "github.com/pkg/errors" + "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/bridge" mattermostbridge "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/bridge/mattermost" xmppbridge "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/bridge/xmpp" @@ -14,11 +20,6 @@ import ( "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger" pluginModel "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/store/kvstore" - "github.com/mattermost/mattermost/server/public/model" - "github.com/mattermost/mattermost/server/public/plugin" - "github.com/mattermost/mattermost/server/public/pluginapi" - "github.com/mattermost/mattermost/server/public/pluginapi/cluster" - "github.com/pkg/errors" ) // Plugin implements the interface expected by the Mattermost server to communicate between the server and plugin processes. @@ -78,7 +79,7 @@ func (p *Plugin) OnActivate() error { p.bridgeManager = bridge.NewBridgeManager(p.logger, p.API, p.remoteID) // Initialize and register bridges with current configuration - if err := p.initBridges(*cfg); err != nil { + if err := p.initBridges(cfg); err != nil { return fmt.Errorf("failed to initialize bridges: %w", err) } @@ -141,13 +142,13 @@ func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*mo return response, nil } -func (p *Plugin) initBridges(cfg config.Configuration) error { +func (p *Plugin) initBridges(cfg *config.Configuration) error { // Create and register XMPP bridge xmppBridge := xmppbridge.NewBridge( p.logger, p.API, p.kvstore, - &cfg, + cfg, "xmpp", p.remoteID, ) @@ -161,7 +162,7 @@ func (p *Plugin) initBridges(cfg config.Configuration) error { p.logger, p.API, p.kvstore, - &cfg, + cfg, p.botUserID, "mattermost", "mattermost", diff --git a/server/plugin_test.go b/server/plugin_test.go index 646318a..3102e45 100644 --- a/server/plugin_test.go +++ b/server/plugin_test.go @@ -10,20 +10,20 @@ import ( ) func TestServeHTTP(t *testing.T) { - assert := assert.New(t) + a := assert.New(t) plugin := Plugin{} w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "/api/v1/hello", nil) + r := httptest.NewRequest(http.MethodGet, "/api/v1/hello", http.NoBody) r.Header.Set("Mattermost-User-ID", "test-user-id") plugin.ServeHTTP(nil, w, r) result := w.Result() - assert.NotNil(result) + a.NotNil(result) defer result.Body.Close() bodyBytes, err := io.ReadAll(result.Body) - assert.Nil(err) + a.Nil(err) bodyString := string(bodyBytes) - assert.Equal("Hello, world!", bodyString) + a.Equal("Hello, world!", bodyString) } diff --git a/server/store/kvstore/constants.go b/server/store/kvstore/constants.go index 698a82c..e14342f 100644 --- a/server/store/kvstore/constants.go +++ b/server/store/kvstore/constants.go @@ -28,4 +28,4 @@ func ExtractIdentifierFromChannelMapKey(key, bridgeName string) string { return "" } return key[len(expectedPrefix):] -} \ No newline at end of file +} diff --git a/server/xmpp/client.go b/server/xmpp/client.go index 586cf65..a021a0e 100644 --- a/server/xmpp/client.go +++ b/server/xmpp/client.go @@ -11,7 +11,6 @@ import ( "time" "github.com/jellydator/ttlcache/v3" - "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger" "mellium.im/sasl" "mellium.im/xmlstream" "mellium.im/xmpp" @@ -20,6 +19,8 @@ import ( "mellium.im/xmpp/muc" "mellium.im/xmpp/mux" "mellium.im/xmpp/stanza" + + "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger" ) const ( @@ -52,7 +53,7 @@ type Client struct { sessionServing bool // Message handling for bridge integration - messageHandler mux.MessageHandlerFunc // Bridge handler for incoming messages + messageHandler mux.MessageHandlerFunc // Bridge handler for incoming messages // Message deduplication cache to handle XMPP server duplicates dedupeCache *ttlcache.Cache[string, time.Time] @@ -80,6 +81,8 @@ type MessageBody struct { } // XMPPMessage represents a complete XMPP message stanza +// +//nolint:revive // XMPPMessage is clearer than Message in this context type XMPPMessage struct { XMLName xml.Name `xml:"jabber:client message"` Type string `xml:"type,attr"` @@ -91,7 +94,7 @@ type XMPPMessage struct { // MessageWithBody represents a message stanza with body for parsing type MessageWithBody struct { stanza.Message - Body string `xml:"body"` + Body string `xml:"body"` } // GhostUser represents an XMPP ghost user @@ -107,7 +110,7 @@ type UserProfile struct { } // NewClient creates a new XMPP client. -func NewClient(serverURL, username, password, resource, remoteID string, logger logger.Logger) *Client { +func NewClient(serverURL, username, password, resource, remoteID string, log logger.Logger) *Client { ctx, cancel := context.WithCancel(context.Background()) // Create TTL cache for message deduplication @@ -119,16 +122,16 @@ func NewClient(serverURL, username, password, resource, remoteID string, logger go dedupeCache.Start() client := &Client{ - serverURL: serverURL, - username: username, - password: password, - resource: resource, - remoteID: remoteID, - logger: logger, - ctx: ctx, - cancel: cancel, - sessionReady: make(chan struct{}), - dedupeCache: dedupeCache, + serverURL: serverURL, + username: username, + password: password, + resource: resource, + remoteID: remoteID, + logger: log, + ctx: ctx, + cancel: cancel, + sessionReady: make(chan struct{}), + dedupeCache: dedupeCache, } // Create MUC client and set up message handling @@ -136,17 +139,17 @@ func NewClient(serverURL, username, password, resource, remoteID string, logger client.mucClient = mucClient // Create mux with MUC client and our message handler - mux := mux.New("jabber:client", + messageMux := mux.New("jabber:client", muc.HandleClient(mucClient), mux.MessageFunc(stanza.GroupChatMessage, xml.Name{}, client.handleIncomingMessage)) - client.mux = mux + client.mux = messageMux return client } // NewClientWithTLS creates a new XMPP client with custom TLS configuration. -func NewClientWithTLS(serverURL, username, password, resource, remoteID string, tlsConfig *tls.Config, logger logger.Logger) *Client { - client := NewClient(serverURL, username, password, resource, remoteID, logger) +func NewClientWithTLS(serverURL, username, password, resource, remoteID string, tlsConfig *tls.Config, log logger.Logger) *Client { + client := NewClient(serverURL, username, password, resource, remoteID, log) client.tlsConfig = tlsConfig return client } @@ -234,7 +237,7 @@ func (c *Client) Connect() error { if c.tlsConfig != nil { tlsConfig = c.tlsConfig } else { - tlsConfig = &tls.Config{ + tlsConfig = &tls.Config{ //nolint:gosec // Default TLS config without MinVersion for XMPP compatibility ServerName: c.jidAddr.Domain().String(), } } @@ -372,15 +375,15 @@ func (c *Client) ExtractChannelID(from jid.JID) (string, error) { } // ExtractUserInfo extracts user ID and display name from a message JID -func (c *Client) ExtractUserInfo(from jid.JID) (string, string) { +func (c *Client) ExtractUserInfo(from jid.JID) (userID, displayName string) { // For MUC messages, the resource part is the nickname nickname := from.Resourcepart() // Use the full JID as user ID for XMPP - userID := from.String() + userID = from.String() // Use nickname as display name if available, otherwise use full JID - displayName := nickname + displayName = nickname if displayName == "" { displayName = from.String() } @@ -486,7 +489,7 @@ func (c *Client) LeaveRoom(roomJID string) error { } // SendMessage sends a message to an XMPP room -func (c *Client) SendMessage(req MessageRequest) (*SendMessageResponse, error) { +func (c *Client) SendMessage(req *MessageRequest) (*SendMessageResponse, error) { if c.session == nil { if err := c.Connect(); err != nil { return nil, err @@ -736,8 +739,9 @@ func (c *Client) Ping() error { return nil } - // handleIncomingMessage processes incoming XMPP message stanzas +// +//nolint:gocritic // msg parameter must match external XMPP library handler signature func (c *Client) handleIncomingMessage(msg stanza.Message, t xmlstream.TokenReadEncoder) error { c.logger.LogDebug("Received XMPP message", "from", msg.From.String(), @@ -770,4 +774,3 @@ func (c *Client) handleIncomingMessage(msg stanza.Message, t xmlstream.TokenRead c.logger.LogDebug("No message handler set, ignoring message") return nil } -