From 9d84faa954cb4588aedda01eae7045816ec4061d Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 13 Jan 2023 17:01:23 +0200 Subject: [PATCH] Add support for unbridging guilds --- database/portal.go | 4 ++++ guildportal.go | 30 ++++++++++++++++++++++++ portal.go | 32 ++++++++++++++++++------- user.go | 58 +++++++++++++++++----------------------------- 4 files changed, 78 insertions(+), 46 deletions(-) diff --git a/database/portal.go b/database/portal.go index d8ce90d..680934c 100644 --- a/database/portal.go +++ b/database/portal.go @@ -56,6 +56,10 @@ func (pq *PortalQuery) GetAll() []*Portal { return pq.getAll(portalSelect) } +func (pq *PortalQuery) GetAllInGuild(guildID string) []*Portal { + return pq.getAll(portalSelect+" WHERE dc_guild_id=$1", guildID) +} + func (pq *PortalQuery) GetByID(key PortalKey) *Portal { return pq.get(portalSelect+" WHERE dcid=$1 AND (receiver=$2 OR receiver='')", key.ChannelID, key.Receiver) } diff --git a/guildportal.go b/guildportal.go index d06e204..325ef6d 100644 --- a/guildportal.go +++ b/guildportal.go @@ -17,6 +17,7 @@ package main import ( + "errors" "fmt" "sync" @@ -285,3 +286,32 @@ func (guild *Guild) UpdateAvatar(iconID string) bool { } return true } + +func (guild *Guild) cleanup() { + if guild.MXID == "" { + return + } + intent := guild.bridge.Bot + if guild.bridge.SpecVersions.UnstableFeatures["com.beeper.room_yeeting"] { + err := intent.BeeperDeleteRoom(guild.MXID) + if err == nil || errors.Is(err, mautrix.MNotFound) { + return + } + guild.log.Warnfln("Failed to delete %s using hungryserv yeet endpoint, falling back to normal behavior: %v", guild.MXID, err) + } + guild.bridge.cleanupRoom(intent, guild.MXID, false, guild.log) +} + +func (guild *Guild) RemoveMXID() { + guild.bridge.guildsLock.Lock() + defer guild.bridge.guildsLock.Unlock() + if guild.MXID == "" { + return + } + delete(guild.bridge.guildsByMXID, guild.MXID) + guild.MXID = "" + guild.AvatarSet = false + guild.NameSet = false + guild.AutoBridgeChannels = false + guild.Update() +} diff --git a/portal.go b/portal.go index 835f08b..ef7cf3c 100644 --- a/portal.go +++ b/portal.go @@ -177,6 +177,10 @@ func (br *DiscordBridge) GetAllPortals() []*Portal { return br.dbPortalsToPortals(br.DB.Portal.GetAll()) } +func (br *DiscordBridge) GetAllPortalsInGuild(guildID string) []*Portal { + return br.dbPortalsToPortals(br.DB.Portal.GetAllInGuild(guildID)) +} + func (br *DiscordBridge) GetAllIPortals() (iportals []bridge.Portal) { portals := br.GetAllPortals() iportals = make([]bridge.Portal, len(portals)) @@ -1291,6 +1295,12 @@ func (portal *Portal) RemoveMXID() { } delete(portal.bridge.portalsByMXID, portal.MXID) portal.MXID = "" + portal.AvatarSet = false + portal.NameSet = false + portal.TopicSet = false + portal.Encrypted = false + portal.InSpace = "" + portal.FirstEventID = "" portal.Update() portal.bridge.DB.Message.DeleteAll(portal.Key) } @@ -1316,9 +1326,13 @@ func (portal *Portal) cleanup(puppetsOnly bool) { return } - members, err := intent.JoinedMembers(portal.MXID) + portal.bridge.cleanupRoom(intent, portal.MXID, puppetsOnly, portal.log) +} + +func (br *DiscordBridge) cleanupRoom(intent *appservice.IntentAPI, mxid id.RoomID, puppetsOnly bool, log log.Logger) { + members, err := intent.JoinedMembers(mxid) if err != nil { - portal.log.Errorln("Failed to get portal members for cleanup:", err) + log.Errorln("Failed to get portal members for cleanup:", err) return } @@ -1327,23 +1341,23 @@ func (portal *Portal) cleanup(puppetsOnly bool) { continue } - puppet := portal.bridge.GetPuppetByMXID(member) + puppet := br.GetPuppetByMXID(member) if puppet != nil { - _, err = puppet.DefaultIntent().LeaveRoom(portal.MXID) + _, err = puppet.DefaultIntent().LeaveRoom(mxid) if err != nil { - portal.log.Errorln("Error leaving as puppet while cleaning up portal:", err) + log.Errorln("Error leaving as puppet while cleaning up portal:", err) } } else if !puppetsOnly { - _, err = intent.KickUser(portal.MXID, &mautrix.ReqKickUser{UserID: member, Reason: "Deleting portal"}) + _, err = intent.KickUser(mxid, &mautrix.ReqKickUser{UserID: member, Reason: "Deleting portal"}) if err != nil { - portal.log.Errorln("Error kicking user while cleaning up portal:", err) + log.Errorln("Error kicking user while cleaning up portal:", err) } } } - _, err = intent.LeaveRoom(portal.MXID) + _, err = intent.LeaveRoom(mxid) if err != nil { - portal.log.Errorln("Error leaving with main intent while cleaning up portal:", err) + log.Errorln("Error leaving with main intent while cleaning up portal:", err) } } diff --git a/user.go b/user.go index 705b1fc..775905c 100644 --- a/user.go +++ b/user.go @@ -1033,41 +1033,25 @@ func (user *User) bridgeGuild(guildID string, everything bool) error { } func (user *User) unbridgeGuild(guildID string) error { - //user.guildsLock.Lock() - //defer user.guildsLock.Unlock() - // - //guild, exists := user.guilds[guildID] - //if !exists { - // return fmt.Errorf("guildID not found") - //} - // - //if !guild.Bridge { - // return fmt.Errorf("guild not bridged") - //} - // - //// First update the guild so we don't have any other go routines recreating - //// channels we're about to destroy. - //guild.Bridge = false - //guild.Upsert() - // - //// Now run through the channels in the guild and remove any portals we - //// have for them. - //channels, err := user.Session.GuildChannels(guildID) - //if err != nil { - // return err - //} - // - //for _, channel := range channels { - // if channelIsBridgeable(channel) { - // key := database.PortalKey{ - // ChannelID: channel.ID, - // Receiver: user.DiscordID, - // } - // - // portal := user.bridge.GetPortalByID(key) - // portal.leave(user) - // } - //} - - return errors.New("unbridging is not currently supported") + if user.PermissionLevel < bridgeconfig.PermissionLevelAdmin { + return errors.New("only bridge admins can unbridge guilds") + } + guild := user.bridge.GetGuildByID(guildID, false) + if guild == nil { + return errors.New("guild not found") + } + guild.roomCreateLock.Lock() + defer guild.roomCreateLock.Unlock() + if !guild.AutoBridgeChannels && guild.MXID == "" { + return errors.New("that guild is not bridged") + } + guild.AutoBridgeChannels = false + guild.Update() + for _, portal := range user.bridge.GetAllPortalsInGuild(guild.ID) { + portal.cleanup(false) + portal.RemoveMXID() + } + guild.cleanup() + guild.RemoveMXID() + return nil }