feat: add /xmppbridge unmap command for channel unmapping
- Add DeleteChannelRoomMapping method to Bridge interface - Implement channel unmapping logic in XMPP bridge (cache + KVStore removal) - Add /xmppbridge unmap command handler with validation - Bridge user automatically leaves XMPP room when unmapping - Update command help text and autocomplete 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
43f0fb1892
commit
5551a8bc8d
3 changed files with 101 additions and 3 deletions
|
@ -441,3 +441,48 @@ func (b *xmppBridge) GetChannelRoomMapping(channelID string) (string, error) {
|
|||
|
||||
return roomJID, nil
|
||||
}
|
||||
|
||||
// DeleteChannelRoomMapping removes a mapping between a Mattermost channel and XMPP room
|
||||
func (b *xmppBridge) DeleteChannelRoomMapping(channelID string) error {
|
||||
if b.kvstore == nil {
|
||||
return fmt.Errorf("KV store not initialized")
|
||||
}
|
||||
|
||||
// Get the room JID from the mapping before deleting
|
||||
roomJID, err := b.GetChannelRoomMapping(channelID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get channel mapping: %w", err)
|
||||
}
|
||||
if roomJID == "" {
|
||||
return fmt.Errorf("channel is not mapped to any room")
|
||||
}
|
||||
|
||||
// Delete forward and reverse mappings from KV store
|
||||
err = b.kvstore.Delete(kvstore.BuildChannelMapKey("mattermost", channelID))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete channel room mapping: %w", err)
|
||||
}
|
||||
|
||||
err = b.kvstore.Delete(kvstore.BuildChannelMapKey("xmpp", roomJID))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete reverse room mapping: %w", err)
|
||||
}
|
||||
|
||||
// Remove from local cache
|
||||
b.mappingsMu.Lock()
|
||||
delete(b.channelMappings, channelID)
|
||||
b.mappingsMu.Unlock()
|
||||
|
||||
// Leave the room if connected
|
||||
if b.connected.Load() && b.xmppClient != nil {
|
||||
if err := b.xmppClient.LeaveRoom(roomJID); err != nil {
|
||||
b.logger.LogWarn("Failed to leave unmapped room", "channel_id", channelID, "room_jid", roomJID, "error", err)
|
||||
// Don't fail the entire operation if leaving the room fails
|
||||
} else {
|
||||
b.logger.LogInfo("Left XMPP room after unmapping", "channel_id", channelID, "room_jid", roomJID)
|
||||
}
|
||||
}
|
||||
|
||||
b.logger.LogInfo("Deleted channel room mapping", "channel_id", channelID, "room_jid", roomJID)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -29,6 +29,9 @@ func NewCommandHandler(client *pluginapi.Client, bridgeManager pluginModel.Bridg
|
|||
mapSubcommand.AddTextArgument("XMPP room JID (e.g., room@conference.example.com)", "[room_jid]", "")
|
||||
xmppBridgeData.AddCommand(mapSubcommand)
|
||||
|
||||
unmapSubcommand := model.NewAutocompleteData("unmap", "", "Unmap current channel from XMPP room")
|
||||
xmppBridgeData.AddCommand(unmapSubcommand)
|
||||
|
||||
statusSubcommand := model.NewAutocompleteData("status", "", "Show bridge connection status")
|
||||
xmppBridgeData.AddCommand(statusSubcommand)
|
||||
|
||||
|
@ -36,7 +39,7 @@ func NewCommandHandler(client *pluginapi.Client, bridgeManager pluginModel.Bridg
|
|||
Trigger: xmppBridgeCommandTrigger,
|
||||
AutoComplete: true,
|
||||
AutoCompleteDesc: "Manage XMPP bridge mappings",
|
||||
AutoCompleteHint: "[map|status]",
|
||||
AutoCompleteHint: "[map|unmap|status]",
|
||||
AutocompleteData: xmppBridgeData,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -72,6 +75,7 @@ func (c *Handler) executeXMPPBridgeCommand(args *model.CommandArgs) *model.Comma
|
|||
|
||||
**Available commands:**
|
||||
- ` + "`/xmppbridge map <room_jid>`" + ` - Map current channel to XMPP room
|
||||
- ` + "`/xmppbridge unmap`" + ` - Unmap current channel from XMPP room
|
||||
- ` + "`/xmppbridge status`" + ` - Show bridge connection status
|
||||
|
||||
**Example:**
|
||||
|
@ -83,6 +87,8 @@ func (c *Handler) executeXMPPBridgeCommand(args *model.CommandArgs) *model.Comma
|
|||
switch subcommand {
|
||||
case "map":
|
||||
return c.executeMapCommand(args, fields)
|
||||
case "unmap":
|
||||
return c.executeUnmapCommand(args)
|
||||
case "status":
|
||||
return c.executeStatusCommand(args)
|
||||
default:
|
||||
|
@ -155,11 +161,54 @@ func (c *Handler) executeMapCommand(args *model.CommandArgs, fields []string) *m
|
|||
}
|
||||
|
||||
return &model.CommandResponse{
|
||||
ResponseType: model.CommandResponseTypeInChannel,
|
||||
ResponseType: model.CommandResponseTypeEphemeral,
|
||||
Text: fmt.Sprintf("✅ Successfully mapped this channel to XMPP room: `%s`", roomJID),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Handler) executeUnmapCommand(args *model.CommandArgs) *model.CommandResponse {
|
||||
channelID := args.ChannelId
|
||||
|
||||
// Get the XMPP bridge
|
||||
bridge, err := c.bridgeManager.GetBridge("xmpp")
|
||||
if err != nil {
|
||||
return &model.CommandResponse{
|
||||
ResponseType: model.CommandResponseTypeEphemeral,
|
||||
Text: "❌ XMPP bridge is not available. Please check the plugin configuration.",
|
||||
}
|
||||
}
|
||||
|
||||
// Check if channel is mapped
|
||||
roomJID, err := bridge.GetChannelRoomMapping(channelID)
|
||||
if err != nil {
|
||||
return &model.CommandResponse{
|
||||
ResponseType: model.CommandResponseTypeEphemeral,
|
||||
Text: fmt.Sprintf("Error checking existing mapping: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
if roomJID == "" {
|
||||
return &model.CommandResponse{
|
||||
ResponseType: model.CommandResponseTypeEphemeral,
|
||||
Text: "❌ This channel is not mapped to any XMPP room.",
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the mapping
|
||||
err = bridge.DeleteChannelRoomMapping(channelID)
|
||||
if err != nil {
|
||||
return &model.CommandResponse{
|
||||
ResponseType: model.CommandResponseTypeEphemeral,
|
||||
Text: fmt.Sprintf("❌ Failed to unmap channel: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
return &model.CommandResponse{
|
||||
ResponseType: model.CommandResponseTypeEphemeral,
|
||||
Text: fmt.Sprintf("✅ Successfully unmapped this channel from XMPP room: `%s`", roomJID),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Handler) executeStatusCommand(args *model.CommandArgs) *model.CommandResponse {
|
||||
// Get the XMPP bridge
|
||||
bridge, err := c.bridgeManager.GetBridge("xmpp")
|
||||
|
@ -201,6 +250,7 @@ func (c *Handler) executeStatusCommand(args *model.CommandArgs) *model.CommandRe
|
|||
%s
|
||||
|
||||
**Commands:**
|
||||
- Use `+"`/xmppbridge map <room_jid>`"+` to map this channel to an XMPP room`, statusText, mappingText),
|
||||
- Use `+"`/xmppbridge map <room_jid>`"+` to map this channel to an XMPP room
|
||||
- Use `+"`/xmppbridge unmap`"+` to unmap this channel from an XMPP room`, statusText, mappingText),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,9 @@ type Bridge interface {
|
|||
// GetChannelRoomMapping retrieves the bridge room ID for a given Mattermost channel ID.
|
||||
GetChannelRoomMapping(channelID string) (string, error)
|
||||
|
||||
// DeleteChannelRoomMapping removes a mapping between a Mattermost channel ID and a bridge room ID.
|
||||
DeleteChannelRoomMapping(channelID string) error
|
||||
|
||||
// IsConnected checks if the bridge is connected to the remote service.
|
||||
IsConnected() bool
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue