chore: fix lint issues
This commit is contained in:
parent
17ea21a579
commit
7c37953c28
20 changed files with 136 additions and 131 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<<uint(i)) * time.Second
|
||||
backoff := time.Duration(1<<i) * time.Second
|
||||
|
||||
select {
|
||||
case <-b.ctx.Done():
|
||||
|
@ -604,6 +605,8 @@ func (b *xmppBridge) ID() string {
|
|||
}
|
||||
|
||||
// handleIncomingXMPPMessage handles incoming XMPP messages and converts them to bridge messages
|
||||
//
|
||||
//nolint:gocritic // msg parameter must match external XMPP library handler signature
|
||||
func (b *xmppBridge) handleIncomingXMPPMessage(msg stanza.Message, t xmlstream.TokenReadEncoder) error {
|
||||
b.logger.LogDebug("XMPP bridge handling incoming message",
|
||||
"from", msg.From.String(),
|
||||
|
|
|
@ -85,7 +85,7 @@ func (h *xmppMessageHandler) sendMessageToXMPP(msg *pluginModel.BridgeMessage) e
|
|||
}
|
||||
|
||||
// Send the message
|
||||
_, err = h.bridge.bridgeClient.SendMessage(req)
|
||||
_, err = h.bridge.bridgeClient.SendMessage(&req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to send message to XMPP room: %w", err)
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ func (r *xmppUserResolver) FormatUserMention(user *pluginModel.ExternalUser) str
|
|||
func (r *xmppUserResolver) GetDisplayName(externalUserID string) string {
|
||||
// For XMPP JIDs, extract the local part or resource as display name
|
||||
// Format: user@domain/resource -> use resource or user
|
||||
if len(externalUserID) == 0 {
|
||||
if externalUserID == "" {
|
||||
return "Unknown User"
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -28,4 +28,4 @@ func ExtractIdentifierFromChannelMapKey(key, bridgeName string) string {
|
|||
return ""
|
||||
}
|
||||
return key[len(expectedPrefix):]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue