feat: implement bridge-agnostic channel mapping keys
- Replace channel mapping keys with bridge-agnostic pattern: channel_map_<bridge>_<identifier> - XMPP mappings now use: channel_map_mattermost_<channelID> → roomJID, channel_map_xmpp_<roomJID> → channelID - Update KV store constants with BuildChannelMapKey() and ExtractIdentifierFromChannelMapKey() - Make KV store completely bridge-agnostic for future Matrix/Discord/Slack bridge support - Fix getAllChannelMappings() to correctly read XMPP keys for room joining on startup - Scalable design supports N bridges with consistent naming pattern 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
8a8c9af611
commit
43f0fb1892
2 changed files with 33 additions and 32 deletions
|
@ -272,28 +272,30 @@ func (b *xmppBridge) getAllChannelMappings() (map[string]string, error) {
|
||||||
|
|
||||||
mappings := make(map[string]string)
|
mappings := make(map[string]string)
|
||||||
|
|
||||||
// Get all keys with the channel mapping prefix
|
// Get all keys with the XMPP room mapping prefix to find all mapped rooms
|
||||||
keys, err := b.kvstore.ListKeysWithPrefix(0, 1000, kvstore.KeyPrefixChannelMapping)
|
xmppPrefix := kvstore.KeyPrefixChannelMap + "xmpp_"
|
||||||
|
keys, err := b.kvstore.ListKeysWithPrefix(0, 1000, xmppPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to list channel mapping keys: %w", err)
|
return nil, fmt.Errorf("failed to list XMPP room mapping keys: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load each mapping
|
// Load each mapping
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
roomJIDBytes, err := b.kvstore.Get(key)
|
channelIDBytes, err := b.kvstore.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.logger.LogWarn("Failed to load mapping for key", "key", key, "error", err)
|
b.logger.LogWarn("Failed to load mapping for key", "key", key, "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract channel ID from the key
|
// Extract room JID from the key
|
||||||
channelID := kvstore.ExtractChannelIDFromKey(key)
|
roomJID := kvstore.ExtractIdentifierFromChannelMapKey(key, "xmpp")
|
||||||
if channelID == "" {
|
if roomJID == "" {
|
||||||
b.logger.LogWarn("Failed to extract channel ID from key", "key", key)
|
b.logger.LogWarn("Failed to extract room JID from key", "key", key)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
mappings[channelID] = string(roomJIDBytes)
|
channelID := string(channelIDBytes)
|
||||||
|
mappings[channelID] = roomJID
|
||||||
}
|
}
|
||||||
|
|
||||||
return mappings, nil
|
return mappings, nil
|
||||||
|
@ -382,13 +384,13 @@ func (b *xmppBridge) CreateChannelRoomMapping(channelID, roomJID string) error {
|
||||||
return fmt.Errorf("KV store not initialized")
|
return fmt.Errorf("KV store not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store forward and reverse mappings
|
// Store forward and reverse mappings using bridge-agnostic keys
|
||||||
err := b.kvstore.Set(kvstore.BuildChannelMappingKey(channelID), []byte(roomJID))
|
err := b.kvstore.Set(kvstore.BuildChannelMapKey("mattermost", channelID), []byte(roomJID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to store channel room mapping: %w", err)
|
return fmt.Errorf("failed to store channel room mapping: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = b.kvstore.Set(kvstore.BuildRoomMappingKey(roomJID), []byte(channelID))
|
err = b.kvstore.Set(kvstore.BuildChannelMapKey("xmpp", roomJID), []byte(channelID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to store reverse room mapping: %w", err)
|
return fmt.Errorf("failed to store reverse room mapping: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -424,8 +426,8 @@ func (b *xmppBridge) GetChannelRoomMapping(channelID string) (string, error) {
|
||||||
return "", fmt.Errorf("KV store not initialized")
|
return "", fmt.Errorf("KV store not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load from KV store
|
// Check if we have a mapping in the KV store for this channel ID
|
||||||
roomJIDBytes, err := b.kvstore.Get(kvstore.BuildChannelMappingKey(channelID))
|
roomJIDBytes, err := b.kvstore.Get(kvstore.BuildChannelMapKey("mattermost", channelID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil // Unmapped channels are expected
|
return "", nil // Unmapped channels are expected
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package kvstore
|
package kvstore
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
// KV Store key prefixes and constants
|
// KV Store key prefixes and constants
|
||||||
// This file centralizes all KV store key patterns used throughout the plugin
|
// This file centralizes all KV store key patterns used throughout the plugin
|
||||||
// to ensure consistency and avoid key conflicts.
|
// to ensure consistency and avoid key conflicts.
|
||||||
|
@ -12,10 +14,8 @@ const (
|
||||||
// KeyPrefixMattermostUser is the prefix for Mattermost user ID -> XMPP user ID mappings
|
// KeyPrefixMattermostUser is the prefix for Mattermost user ID -> XMPP user ID mappings
|
||||||
KeyPrefixMattermostUser = "mattermost_user_"
|
KeyPrefixMattermostUser = "mattermost_user_"
|
||||||
|
|
||||||
// KeyPrefixChannelMapping is the prefix for Mattermost channel ID -> XMPP room mappings
|
// KeyPrefixChannelMap is the prefix for bridge-agnostic channel mappings
|
||||||
KeyPrefixChannelMapping = "channel_mapping_"
|
KeyPrefixChannelMap = "channel_map_"
|
||||||
// KeyPrefixRoomMapping is the prefix for XMPP room identifier -> Mattermost channel ID mappings
|
|
||||||
KeyPrefixRoomMapping = "xmpp_room_mapping_"
|
|
||||||
|
|
||||||
// KeyPrefixGhostUser is the prefix for Mattermost user ID -> XMPP ghost user ID cache
|
// KeyPrefixGhostUser is the prefix for Mattermost user ID -> XMPP ghost user ID cache
|
||||||
KeyPrefixGhostUser = "ghost_user_"
|
KeyPrefixGhostUser = "ghost_user_"
|
||||||
|
@ -30,9 +30,9 @@ const (
|
||||||
// KeyStoreVersion is the key for tracking the current KV store schema version
|
// KeyStoreVersion is the key for tracking the current KV store schema version
|
||||||
KeyStoreVersion = "kv_store_version"
|
KeyStoreVersion = "kv_store_version"
|
||||||
|
|
||||||
// KeyPrefixLegacyDMMapping was the old prefix for DM mappings (migrated to channel_mapping_)
|
// KeyPrefixLegacyDMMapping was the old prefix for DM mappings
|
||||||
KeyPrefixLegacyDMMapping = "dm_mapping_"
|
KeyPrefixLegacyDMMapping = "dm_mapping_"
|
||||||
// KeyPrefixLegacyXMPPDMMapping was the old prefix for XMPP DM mappings (migrated to room_mapping_)
|
// KeyPrefixLegacyXMPPDMMapping was the old prefix for XMPP DM mappings
|
||||||
KeyPrefixLegacyXMPPDMMapping = "xmpp_dm_mapping_"
|
KeyPrefixLegacyXMPPDMMapping = "xmpp_dm_mapping_"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,14 +48,9 @@ func BuildMattermostUserKey(mattermostUserID string) string {
|
||||||
return KeyPrefixMattermostUser + mattermostUserID
|
return KeyPrefixMattermostUser + mattermostUserID
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildChannelMappingKey creates a key for channel -> room mapping
|
// BuildChannelMapKey creates a bridge-agnostic key for channel mappings
|
||||||
func BuildChannelMappingKey(channelID string) string {
|
func BuildChannelMapKey(bridgeName, identifier string) string {
|
||||||
return KeyPrefixChannelMapping + channelID
|
return KeyPrefixChannelMap + bridgeName + "_" + identifier
|
||||||
}
|
|
||||||
|
|
||||||
// BuildRoomMappingKey creates a key for room -> channel mapping
|
|
||||||
func BuildRoomMappingKey(roomIdentifier string) string {
|
|
||||||
return KeyPrefixRoomMapping + roomIdentifier
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildGhostUserKey creates a key for ghost user cache
|
// BuildGhostUserKey creates a key for ghost user cache
|
||||||
|
@ -78,10 +73,14 @@ func BuildXMPPReactionKey(reactionEventID string) string {
|
||||||
return KeyPrefixXMPPReaction + reactionEventID
|
return KeyPrefixXMPPReaction + reactionEventID
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractChannelIDFromKey extracts the channel ID from a channel mapping key
|
// ExtractIdentifierFromChannelMapKey extracts the identifier from a bridge-agnostic channel map key
|
||||||
func ExtractChannelIDFromKey(key string) string {
|
func ExtractIdentifierFromChannelMapKey(key, bridgeName string) string {
|
||||||
if len(key) <= len(KeyPrefixChannelMapping) {
|
expectedPrefix := KeyPrefixChannelMap + bridgeName + "_"
|
||||||
|
if len(key) <= len(expectedPrefix) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return key[len(KeyPrefixChannelMapping):]
|
if !strings.HasPrefix(key, expectedPrefix) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return key[len(expectedPrefix):]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue