chore: fix lint issues
This commit is contained in:
parent
17ea21a579
commit
7c37953c28
20 changed files with 136 additions and 131 deletions
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue