Add bridge/unbridge/delete-portal commands

Fixes #34
This commit is contained in:
Tulir Asokan
2023-03-04 14:11:05 +02:00
parent 0e8b845014
commit 3c52e76e15
2 changed files with 164 additions and 12 deletions

View File

@@ -32,6 +32,7 @@ import (
"maunium.net/go/mautrix" "maunium.net/go/mautrix"
"maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/appservice"
"maunium.net/go/mautrix/bridge/bridgeconfig"
"maunium.net/go/mautrix/bridge/commands" "maunium.net/go/mautrix/bridge/commands"
"maunium.net/go/mautrix/event" "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
@@ -58,6 +59,9 @@ func (br *DiscordBridge) RegisterCommands() {
cmdPing, cmdPing,
cmdReconnect, cmdReconnect,
cmdDisconnect, cmdDisconnect,
cmdBridge,
cmdUnbridge,
cmdDeletePortal,
cmdSetRelay, cmdSetRelay,
cmdUnsetRelay, cmdUnsetRelay,
cmdGuilds, cmdGuilds,
@@ -406,14 +410,16 @@ func fnSetRelay(ce *WrappedCommandEvent) {
ce.Reply("Portal with room ID %s not found", ce.Args[0]) ce.Reply("Portal with room ID %s not found", ce.Args[0])
return return
} }
levels, err := portal.MainIntent().PowerLevels(ce.RoomID) if ce.User.PermissionLevel < bridgeconfig.PermissionLevelAdmin {
if err != nil { levels, err := portal.MainIntent().PowerLevels(ce.RoomID)
ce.ZLog.Warn().Err(err).Msg("Failed to check room power levels") if err != nil {
ce.Reply("Failed to get room power levels to see if you're allowed to use that command") ce.ZLog.Warn().Err(err).Msg("Failed to check room power levels")
return ce.Reply("Failed to get room power levels to see if you're allowed to use that command")
} else if levels.GetUserLevel(ce.User.GetMXID()) < levels.GetEventLevel(roomModerator) { return
ce.Reply("You don't have admin rights in that room") } else if levels.GetUserLevel(ce.User.GetMXID()) < levels.GetEventLevel(roomModerator) {
return ce.Reply("You don't have admin rights in that room")
return
}
} }
ce.Args = ce.Args[1:] ce.Args = ce.Args[1:]
} else if portal == nil { } else if portal == nil {
@@ -640,6 +646,142 @@ func fnGuildBridgingMode(ce *WrappedCommandEvent) {
ce.Reply("Set guild bridging mode to %s", mode.Description()) ce.Reply("Set guild bridging mode to %s", mode.Description())
} }
var cmdBridge = &commands.FullHandler{
Func: wrapCommand(fnBridge),
Name: "bridge",
Help: commands.HelpMeta{
Section: HelpSectionPortalManagement,
Description: "Bridge this room to a specific Discord channel",
Args: "[--replace[=delete]] <_channel ID_>",
},
RequiresEventLevel: roomModerator,
}
func isNumber(str string) bool {
for _, chr := range str {
if chr < '0' || chr > '9' {
return false
}
}
return true
}
func fnBridge(ce *WrappedCommandEvent) {
if ce.Portal != nil {
ce.Reply("This is already a portal room. Unbridge with `$cmdprefix unbridge` first if you want to link it to a different channel.")
return
}
var channelID string
var unbridgeOld, deleteOld bool
fail := true
for _, arg := range ce.Args {
arg = strings.ToLower(arg)
if arg == "--replace" {
unbridgeOld = true
} else if arg == "--replace=delete" {
unbridgeOld = true
deleteOld = true
} else if channelID == "" && isNumber(arg) {
channelID = arg
fail = false
} else {
fail = true
break
}
}
if fail {
ce.Reply("**Usage**: `$cmdprefix bridge [--replace[=delete]] <channel ID>`")
return
}
portal := ce.User.GetExistingPortalByID(channelID)
if portal == nil {
ce.Reply("Channel not found")
return
}
portal.roomCreateLock.Lock()
defer portal.roomCreateLock.Lock()
if portal.MXID != "" {
hasUnbridgePermission := ce.User.PermissionLevel >= bridgeconfig.PermissionLevelAdmin
if !hasUnbridgePermission {
levels, err := portal.MainIntent().PowerLevels(portal.MXID)
if errors.Is(err, mautrix.MNotFound) {
ce.ZLog.Debug().Err(err).Msg("Got M_NOT_FOUND trying to get power levels to check if user can unbridge it, assuming the room is gone")
hasUnbridgePermission = true
} else if err != nil {
ce.ZLog.Warn().Err(err).Msg("Failed to check room power levels")
ce.Reply("Failed to get power levels in old room to see if you're allowed to unbridge it")
return
} else {
hasUnbridgePermission = levels.GetUserLevel(ce.User.GetMXID()) >= levels.GetEventLevel(roomModerator)
}
}
if !unbridgeOld || !hasUnbridgePermission {
extraHelp := "Rerun the command with `--replace` or `--replace=delete` to unbridge the old room."
if !hasUnbridgePermission {
extraHelp = "Additionally, you do not have the permissions to unbridge the old room."
}
ce.Reply("That channel is already bridged to [%s](https://matrix.to/#/%s). %s", portal.Name, portal.MXID, extraHelp)
return
}
ce.ZLog.Debug().
Str("old_room_id", portal.MXID.String()).
Bool("delete", deleteOld).
Msg("Unbridging old room")
portal.removeFromSpace()
portal.RemoveMXID()
portal.cleanup(deleteOld)
ce.ZLog.Info().
Str("old_room_id", portal.MXID.String()).
Bool("delete", deleteOld).
Msg("Unbridged old room to make space for new bridge")
}
if portal.Guild != nil && portal.Guild.BridgingMode < database.GuildBridgeIfPortalExists {
ce.ZLog.Debug().Str("guild_id", portal.Guild.ID).Msg("Bumping bridging mode of portal guild to if-portal-exists")
portal.Guild.BridgingMode = database.GuildBridgeIfPortalExists
portal.Guild.Update()
}
ce.ZLog.Debug().Str("channel_id", portal.Key.ChannelID).Msg("Bridging room")
portal.MXID = ce.RoomID
portal.updateRoomName()
portal.updateRoomAvatar()
portal.updateRoomTopic()
portal.updateSpace()
portal.UpdateBridgeInfo()
portal.Update()
ce.Reply("Room successfully bridged")
ce.ZLog.Info().Str("channel_id", portal.Key.ChannelID).Msg("Manual bridging complete")
}
var cmdUnbridge = &commands.FullHandler{
Func: wrapCommand(fnUnbridge),
Name: "unbridge",
Help: commands.HelpMeta{
Section: HelpSectionPortalManagement,
Description: "Unbridge this room from the linked Discord channel",
},
RequiresPortal: true,
RequiresEventLevel: roomModerator,
}
var cmdDeletePortal = &commands.FullHandler{
Func: wrapCommand(fnUnbridge),
Name: "delete-portal",
Help: commands.HelpMeta{
Section: HelpSectionPortalManagement,
Description: "Unbridge this room and kick all Matrix users",
},
RequiresPortal: true,
RequiresEventLevel: roomModerator,
}
func fnUnbridge(ce *WrappedCommandEvent) {
ce.Portal.roomCreateLock.Lock()
defer ce.Portal.roomCreateLock.Lock()
ce.Portal.removeFromSpace()
ce.Portal.RemoveMXID()
ce.Portal.cleanup(ce.Command == "delete-portal")
}
var cmdDeleteAllPortals = &commands.FullHandler{ var cmdDeleteAllPortals = &commands.FullHandler{
Func: wrapCommand(fnDeleteAllPortals), Func: wrapCommand(fnDeleteAllPortals),
Name: "delete-all-portals", Name: "delete-all-portals",

View File

@@ -160,7 +160,9 @@ func (br *DiscordBridge) GetExistingPortalByID(key database.PortalKey) *Portal {
defer br.portalsLock.Unlock() defer br.portalsLock.Unlock()
portal, ok := br.portalsByID[key] portal, ok := br.portalsByID[key]
if !ok { if !ok {
portal, ok = br.portalsByID[database.NewPortalKey(key.ChannelID, "")] if key.Receiver != "" {
portal, ok = br.portalsByID[database.NewPortalKey(key.ChannelID, "")]
}
if !ok { if !ok {
return br.loadPortal(br.DB.Portal.GetByID(key), nil, -1) return br.loadPortal(br.DB.Portal.GetByID(key), nil, -1)
} }
@@ -1732,6 +1734,11 @@ func (portal *Portal) UpdateNameDirect(name string) bool {
portal.log.Debugfln("Updating name %q -> %q", portal.Name, name) portal.log.Debugfln("Updating name %q -> %q", portal.Name, name)
portal.Name = name portal.Name = name
portal.NameSet = false portal.NameSet = false
portal.updateRoomName()
return true
}
func (portal *Portal) updateRoomName() {
if portal.MXID != "" { if portal.MXID != "" {
_, err := portal.MainIntent().SetRoomName(portal.MXID, portal.Name) _, err := portal.MainIntent().SetRoomName(portal.MXID, portal.Name)
if err != nil { if err != nil {
@@ -1740,7 +1747,6 @@ func (portal *Portal) UpdateNameDirect(name string) bool {
portal.NameSet = true portal.NameSet = true
} }
} }
return true
} }
func (portal *Portal) UpdateAvatarFromPuppet(puppet *Puppet) bool { func (portal *Portal) UpdateAvatarFromPuppet(puppet *Puppet) bool {
@@ -1779,7 +1785,7 @@ func (portal *Portal) UpdateGroupDMAvatar(iconID string) bool {
} }
func (portal *Portal) updateRoomAvatar() { func (portal *Portal) updateRoomAvatar() {
if portal.MXID == "" { if portal.MXID == "" || portal.AvatarURL.IsEmpty() {
return return
} }
_, err := portal.MainIntent().SetRoomAvatar(portal.MXID, portal.AvatarURL) _, err := portal.MainIntent().SetRoomAvatar(portal.MXID, portal.AvatarURL)
@@ -1797,6 +1803,11 @@ func (portal *Portal) UpdateTopic(topic string) bool {
portal.log.Debugfln("Updating topic %q -> %q", portal.Topic, topic) portal.log.Debugfln("Updating topic %q -> %q", portal.Topic, topic)
portal.Topic = topic portal.Topic = topic
portal.TopicSet = false portal.TopicSet = false
portal.updateRoomTopic()
return true
}
func (portal *Portal) updateRoomTopic() {
if portal.MXID != "" { if portal.MXID != "" {
_, err := portal.MainIntent().SetRoomTopic(portal.MXID, portal.Topic) _, err := portal.MainIntent().SetRoomTopic(portal.MXID, portal.Topic)
if err != nil { if err != nil {
@@ -1805,7 +1816,6 @@ func (portal *Portal) UpdateTopic(topic string) bool {
portal.TopicSet = true portal.TopicSet = true
} }
} }
return true
} }
func (portal *Portal) removeFromSpace() { func (portal *Portal) removeFromSpace() {