From 5afd40fc63b1fb3d9da95f5103391161e9c373e1 Mon Sep 17 00:00:00 2001 From: s3lph <5564491+s3lph@users.noreply.github.com> Date: Sun, 27 Sep 2020 01:34:09 +0200 Subject: [PATCH] Fix channel name handling (support channel IDs and full channel paths) --- bridge/mumble/handlers.go | 4 ++-- bridge/mumble/helpers.go | 49 +++++++++++++++++++++++++++++++++++++-- bridge/mumble/mumble.go | 22 +++++++++++------- matterbridge.toml.sample | 3 ++- 4 files changed, 64 insertions(+), 14 deletions(-) diff --git a/bridge/mumble/handlers.go b/bridge/mumble/handlers.go index 487c3276..8d7b8cc2 100644 --- a/bridge/mumble/handlers.go +++ b/bridge/mumble/handlers.go @@ -53,7 +53,7 @@ func (b *Bmumble) handleConnect(event *gumble.ConnectEvent) { event.Client.Self.SetSelfDeafened(true) event.Client.Self.SetSelfMuted(true) // if the Channel variable is set, this is a reconnect -> rejoin channel - if b.Channel != "" { + if b.Channel != nil { if err := b.doJoin(event.Client, b.Channel); err != nil { b.Log.Error(err) } @@ -73,7 +73,7 @@ func (b *Bmumble) handleUserChange(event *gumble.UserChangeEvent) { return } // Someone attempted to move the user out of the configured channel; attempt to join back - if b.Channel != "" && b.Channel != event.Client.Self.Channel.Name { + if b.Channel != nil { if err := b.doJoin(event.Client, b.Channel); err != nil { b.Log.Error(err) } diff --git a/bridge/mumble/helpers.go b/bridge/mumble/helpers.go index 78572d25..3eccda66 100644 --- a/bridge/mumble/helpers.go +++ b/bridge/mumble/helpers.go @@ -1,12 +1,18 @@ package bmumble import ( + "errors" "fmt" "mime" "net/http" + "net/url" "regexp" + "strconv" "strings" + "layeh.com/gumble/gumble" + "layeh.com/gumble/gumbleutil" + "github.com/42wim/matterbridge/bridge/config" "github.com/mattn/godown" "github.com/vincent-petithory/dataurl" @@ -47,8 +53,6 @@ func (b *Bmumble) tokenize(t *string) ([]MessagePart, error) { var parts []MessagePart for { tokens := p.FindStringSubmatch(remaining) - b.Log.Debugf("### tokens: %#v", tokens) - if tokens == nil { // no match -> remaining string is non-image text pre := strings.TrimSpace(remaining) @@ -57,6 +61,7 @@ func (b *Bmumble) tokenize(t *string) ([]MessagePart, error) { } return parts, nil } + // tokens[1] is the text before the image if len(tokens[1]) > 0 { pre := strings.TrimSpace(tokens[1]) @@ -142,3 +147,43 @@ func (b *Bmumble) extractFiles(msg *config.Message) []config.Message { msg.Extra["file"] = nil return messages } + +func (b *Bmumble) parseChannelPath(client *gumble.Client, name string) ([]string, error) { + if strings.HasPrefix(name, "ID:") { + if channelId, err := strconv.ParseUint(name[3:], 10, 32); err == nil { + if c, ok := client.Channels[uint32(channelId)]; ok { + return gumbleutil.ChannelPath(c)[1:], nil + } + b.Log.Fatalf("No channel with ID %d", channelId) + return nil, errors.New("no such channel: " + name) + } else { + b.Log.WithError(err).Fatalf("Cannot parse channel ID: %s", name) + return nil, err + } + } else { + if !strings.HasPrefix(name, "/") { + return nil, errors.New("channel path must start with a '/': " + name) + } + // Special treatment for the root channel: empty slice + if name == "/" { + return make([]string, 0), nil + } + // Discard first token, which is the empty string before the leading / + tokens := strings.Split(name, "/") + var channelPath []string + for _, token := range tokens[1:] { + // Urldecode each token and append it to the path + if channelName, err := url.PathUnescape(token); err == nil && len(channelName) > 0 { + channelPath = append(channelPath, channelName) + } else { + b.Log.WithError(err).Fatalf("Error while decoding path component '%s'", token) + return nil, err + } + } + c := client.Channels.Find(channelPath...) + if c == nil { + return nil, errors.New("no such channel: " + name) + } + return gumbleutil.ChannelPath(c)[1:], nil + } +} diff --git a/bridge/mumble/mumble.go b/bridge/mumble/mumble.go index ea3eb54e..cb902c34 100644 --- a/bridge/mumble/mumble.go +++ b/bridge/mumble/mumble.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "crypto/x509" "errors" + "fmt" "io/ioutil" "net" "strconv" @@ -26,7 +27,7 @@ type Bmumble struct { client *gumble.Client Nick string Host string - Channel string + Channel []string local chan config.Message running chan error connected chan gumble.DisconnectEvent @@ -75,12 +76,15 @@ func (b *Bmumble) Disconnect() error { } func (b *Bmumble) JoinChannel(channel config.ChannelInfo) error { - if b.Channel != "" && channel.Name != b.Channel { - b.Log.Fatalf("Cannot join channel '%s', already joined to channel '%s'", channel.Name, b.Channel) + channelPath, err := b.parseChannelPath(b.client, channel.Name) + if err != nil { + return err + } + if b.Channel != nil { + b.Log.Fatalf("Cannot join channel '%v', already joined to channel '%v'", channel.Name, b.Channel) return errors.New("the Mumble bridge can only join a single channel") } - b.Channel = channel.Name - return b.doJoin(b.client, channel.Name) + return b.doJoin(b.client, channelPath) } func (b *Bmumble) Send(msg config.Message) (string, error) { @@ -186,13 +190,13 @@ func (b *Bmumble) doConnect() error { return nil } -func (b *Bmumble) doJoin(client *gumble.Client, name string) error { - c := client.Channels.Find(name) +func (b *Bmumble) doJoin(client *gumble.Client, channelPath []string) error { + c := client.Channels.Find(channelPath...) if c == nil { - return errors.New("No such channel: " + name) + return errors.New(fmt.Sprintf("no such channel: %v", channelPath)) } + b.Channel = gumbleutil.ChannelPath(c)[1:] client.Self.Move(c) - b.Channel = c.Name return nil } diff --git a/matterbridge.toml.sample b/matterbridge.toml.sample index 244b6b40..a979ef97 100644 --- a/matterbridge.toml.sample +++ b/matterbridge.toml.sample @@ -1791,7 +1791,8 @@ enable=true # ------------------------------------------------------------------------------------------------------------------------------------- # msteams | threadId | 19:82abcxx@thread.skype | You'll find the threadId in the URL # ------------------------------------------------------------------------------------------------------------------------------------- - # mumble | channel name | General | The channel name, as shown in Mumble + # mumble | channel id | ID:42 | The channel ID, as shown in the channel's "Edit" window + # | channel path | /A%20channel/a%20subchannel | The urlencoded channel path, as contained in the channel's URL # ------------------------------------------------------------------------------------------------------------------------------------- # rocketchat | channel | #channel | # is required for private channels too # -------------------------------------------------------------------------------------------------------------------------------------