package command 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/pluginapi" ) type Handler struct { client *pluginapi.Client bridgeManager pluginModel.BridgeManager } type Command interface { Handle(args *model.CommandArgs) (*model.CommandResponse, error) executeXMPPBridgeCommand(args *model.CommandArgs) *model.CommandResponse } const xmppBridgeCommandTrigger = "xmppbridge" // Register all your slash commands in the NewCommandHandler function. func NewCommandHandler(client *pluginapi.Client, bridgeManager pluginModel.BridgeManager) Command { // Register XMPP bridge command xmppBridgeData := model.NewAutocompleteData(xmppBridgeCommandTrigger, "", "Manage XMPP bridge") mapSubcommand := model.NewAutocompleteData("map", "[room_jid]", "Map current channel to XMPP room") mapSubcommand.AddTextArgument("XMPP room JID (e.g., room@conference.example.com)", "[room_jid]", "") xmppBridgeData.AddCommand(mapSubcommand) statusSubcommand := model.NewAutocompleteData("status", "", "Show bridge connection status") xmppBridgeData.AddCommand(statusSubcommand) err := client.SlashCommand.Register(&model.Command{ Trigger: xmppBridgeCommandTrigger, AutoComplete: true, AutoCompleteDesc: "Manage XMPP bridge mappings", AutoCompleteHint: "[map|status]", AutocompleteData: xmppBridgeData, }) if err != nil { client.Log.Error("Failed to register XMPP bridge command", "error", err) } return &Handler{ client: client, bridgeManager: bridgeManager, } } // ExecuteCommand hook calls this method to execute the commands that were registered in the NewCommandHandler function. func (c *Handler) Handle(args *model.CommandArgs) (*model.CommandResponse, error) { trigger := strings.TrimPrefix(strings.Fields(args.Command)[0], "/") switch trigger { case xmppBridgeCommandTrigger: return c.executeXMPPBridgeCommand(args), nil default: return &model.CommandResponse{ ResponseType: model.CommandResponseTypeEphemeral, Text: fmt.Sprintf("Unknown command: %s", args.Command), }, nil } } func (c *Handler) executeXMPPBridgeCommand(args *model.CommandArgs) *model.CommandResponse { fields := strings.Fields(args.Command) if len(fields) < 2 { return &model.CommandResponse{ ResponseType: model.CommandResponseTypeEphemeral, Text: `### XMPP Bridge Commands **Available commands:** - ` + "`/xmppbridge map `" + ` - Map current channel to XMPP room - ` + "`/xmppbridge status`" + ` - Show bridge connection status **Example:** ` + "`/xmppbridge map general@conference.example.com`", } } subcommand := fields[1] switch subcommand { case "map": return c.executeMapCommand(args, fields) case "status": return c.executeStatusCommand(args) default: return &model.CommandResponse{ ResponseType: model.CommandResponseTypeEphemeral, Text: fmt.Sprintf("Unknown subcommand: %s. Use `/xmppbridge` for help.", subcommand), } } } func (c *Handler) executeMapCommand(args *model.CommandArgs, fields []string) *model.CommandResponse { if len(fields) < 3 { return &model.CommandResponse{ ResponseType: model.CommandResponseTypeEphemeral, Text: "Please specify an XMPP room JID. Example: `/xmppbridge map general@conference.example.com`", } } roomJID := fields[2] channelID := args.ChannelId // Validate room JID format (basic validation) if !strings.Contains(roomJID, "@") { return &model.CommandResponse{ ResponseType: model.CommandResponseTypeEphemeral, Text: "Invalid room JID format. Please use format: `room@conference.server.com`", } } // 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 bridge is connected if !bridge.IsConnected() { return &model.CommandResponse{ ResponseType: model.CommandResponseTypeEphemeral, Text: "❌ XMPP bridge is not connected. Please check the plugin configuration.", } } // Check if channel is already mapped existingMapping, err := bridge.GetChannelRoomMapping(channelID) if err != nil { return &model.CommandResponse{ ResponseType: model.CommandResponseTypeEphemeral, Text: fmt.Sprintf("Error checking existing mapping: %v", err), } } if existingMapping != "" { return &model.CommandResponse{ ResponseType: model.CommandResponseTypeEphemeral, Text: fmt.Sprintf("❌ This channel is already mapped to XMPP room: `%s`", existingMapping), } } // Create the mapping err = bridge.CreateChannelRoomMapping(channelID, roomJID) if err != nil { return &model.CommandResponse{ ResponseType: model.CommandResponseTypeEphemeral, Text: fmt.Sprintf("❌ Failed to create channel mapping: %v", err), } } return &model.CommandResponse{ ResponseType: model.CommandResponseTypeInChannel, Text: fmt.Sprintf("✅ Successfully mapped this channel to XMPP room: `%s`", roomJID), } } func (c *Handler) executeStatusCommand(args *model.CommandArgs) *model.CommandResponse { // 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.", } } isConnected := bridge.IsConnected() var statusText string if isConnected { statusText = "✅ **Connected** - XMPP bridge is active" } else { statusText = "❌ **Disconnected** - XMPP bridge is not connected" } // Check if current channel is mapped channelID := args.ChannelId roomJID, err := bridge.GetChannelRoomMapping(channelID) var mappingText string if err != nil { mappingText = fmt.Sprintf("⚠️ Error checking channel mapping: %v", err) } else if roomJID != "" { mappingText = fmt.Sprintf("🔗 **Current channel mapping:** `%s`", roomJID) } else { mappingText = "📝 **Current channel:** Not mapped to any XMPP room" } return &model.CommandResponse{ ResponseType: model.CommandResponseTypeEphemeral, Text: fmt.Sprintf(`### XMPP Bridge Status %s %s **Commands:** - Use `+"`/xmppbridge map `"+` to map this channel to an XMPP room`, statusText, mappingText), } }