mirror of https://github.com/mautrix/go.git
190 lines
5.6 KiB
Go
190 lines
5.6 KiB
Go
// Copyright (c) 2024 Tulir Asokan
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package bridgev2
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"maunium.net/go/mautrix"
|
|
"maunium.net/go/mautrix/bridgev2/database"
|
|
"maunium.net/go/mautrix/event"
|
|
"maunium.net/go/mautrix/id"
|
|
)
|
|
|
|
func (ul *UserLogin) MarkInPortal(ctx context.Context, portal *Portal) {
|
|
if ul.inPortalCache.Has(portal.PortalKey) {
|
|
return
|
|
}
|
|
userPortal, err := ul.Bridge.DB.UserPortal.GetOrCreate(ctx, ul.UserLogin, portal.PortalKey)
|
|
if err != nil {
|
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to ensure user portal row exists")
|
|
return
|
|
}
|
|
ul.inPortalCache.Add(portal.PortalKey)
|
|
if portal.MXID != "" {
|
|
dp := ul.User.DoublePuppet(ctx)
|
|
if dp != nil {
|
|
err = dp.EnsureJoined(ctx, portal.MXID)
|
|
if err != nil {
|
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to ensure double puppet is joined to portal")
|
|
}
|
|
} else {
|
|
err = ul.Bridge.Bot.EnsureInvited(ctx, portal.MXID, ul.UserMXID)
|
|
if err != nil {
|
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to ensure user is invited to portal")
|
|
}
|
|
}
|
|
if ul.Bridge.Config.PersonalFilteringSpaces && (userPortal.InSpace == nil || !*userPortal.InSpace) {
|
|
go ul.tryAddPortalToSpace(context.WithoutCancel(ctx), portal, userPortal.CopyWithoutValues())
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ul *UserLogin) tryAddPortalToSpace(ctx context.Context, portal *Portal, userPortal *database.UserPortal) {
|
|
err := ul.AddPortalToSpace(ctx, portal, userPortal)
|
|
if err != nil {
|
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to add portal to space")
|
|
}
|
|
}
|
|
|
|
func (ul *UserLogin) AddPortalToSpace(ctx context.Context, portal *Portal, userPortal *database.UserPortal) error {
|
|
if portal.MXID == "" || portal.Parent != nil {
|
|
return nil
|
|
}
|
|
spaceRoom, err := ul.GetSpaceRoom(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get space room: %w", err)
|
|
} else if spaceRoom == "" {
|
|
return nil
|
|
}
|
|
err = portal.toggleSpace(ctx, spaceRoom, false, false)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to add portal to space: %w", err)
|
|
}
|
|
inSpace := true
|
|
userPortal.InSpace = &inSpace
|
|
err = ul.Bridge.DB.UserPortal.Put(ctx, userPortal)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save user portal row: %w", err)
|
|
}
|
|
zerolog.Ctx(ctx).Debug().Stringer("space_room_id", spaceRoom).Msg("Added portal to space")
|
|
return nil
|
|
}
|
|
|
|
func (portal *Portal) createParentAndAddToSpace(ctx context.Context, source *UserLogin) {
|
|
err := portal.Parent.CreateMatrixRoom(ctx, source, nil)
|
|
if err != nil {
|
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to create parent portal")
|
|
} else {
|
|
portal.addToParentSpaceAndSave(ctx, true)
|
|
}
|
|
}
|
|
|
|
func (portal *Portal) addToParentSpaceAndSave(ctx context.Context, save bool) {
|
|
err := portal.toggleSpace(ctx, portal.Parent.MXID, true, false)
|
|
if err != nil {
|
|
zerolog.Ctx(ctx).Err(err).Stringer("space_mxid", portal.Parent.MXID).Msg("Failed to add portal to space")
|
|
} else {
|
|
portal.InSpace = true
|
|
if save {
|
|
err = portal.Save(ctx)
|
|
if err != nil {
|
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to save portal to database after adding to space")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (portal *Portal) toggleSpace(ctx context.Context, spaceID id.RoomID, canonical, remove bool) error {
|
|
via := []string{portal.Bridge.Matrix.ServerName()}
|
|
if remove {
|
|
via = nil
|
|
}
|
|
_, err := portal.Bridge.Bot.SendState(ctx, spaceID, event.StateSpaceChild, portal.MXID.String(), &event.Content{
|
|
Parsed: &event.SpaceChildEventContent{
|
|
Via: via,
|
|
},
|
|
}, time.Now())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if canonical {
|
|
_, err = portal.Bridge.Bot.SendState(ctx, portal.MXID, event.StateSpaceParent, spaceID.String(), &event.Content{
|
|
Parsed: &event.SpaceParentEventContent{
|
|
Via: via,
|
|
Canonical: !remove,
|
|
},
|
|
}, time.Now())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ul *UserLogin) GetSpaceRoom(ctx context.Context) (id.RoomID, error) {
|
|
if !ul.Bridge.Config.PersonalFilteringSpaces {
|
|
return ul.SpaceRoom, nil
|
|
}
|
|
ul.spaceCreateLock.Lock()
|
|
defer ul.spaceCreateLock.Unlock()
|
|
if ul.SpaceRoom != "" {
|
|
return ul.SpaceRoom, nil
|
|
}
|
|
netName := ul.Bridge.Network.GetName()
|
|
var err error
|
|
autoJoin := ul.Bridge.Matrix.GetCapabilities().AutoJoinInvites
|
|
doublePuppet := ul.User.DoublePuppet(ctx)
|
|
req := &mautrix.ReqCreateRoom{
|
|
Visibility: "private",
|
|
Name: fmt.Sprintf("%s (%s)", netName.DisplayName, ul.RemoteName),
|
|
Topic: fmt.Sprintf("Your %s bridged chats - %s", netName.DisplayName, ul.RemoteName),
|
|
InitialState: []*event.Event{{
|
|
Type: event.StateRoomAvatar,
|
|
Content: event.Content{
|
|
Parsed: &event.RoomAvatarEventContent{
|
|
URL: netName.NetworkIcon,
|
|
},
|
|
},
|
|
}},
|
|
CreationContent: map[string]any{
|
|
"type": event.RoomTypeSpace,
|
|
},
|
|
PowerLevelOverride: &event.PowerLevelsEventContent{
|
|
Users: map[id.UserID]int{
|
|
ul.Bridge.Bot.GetMXID(): 9001,
|
|
ul.UserMXID: 50,
|
|
},
|
|
},
|
|
Invite: []id.UserID{ul.UserMXID},
|
|
}
|
|
if autoJoin {
|
|
req.BeeperInitialMembers = []id.UserID{ul.UserMXID}
|
|
// TODO remove this after initial_members is supported in hungryserv
|
|
req.BeeperAutoJoinInvites = true
|
|
}
|
|
ul.SpaceRoom, err = ul.Bridge.Bot.CreateRoom(ctx, req)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create space room: %w", err)
|
|
}
|
|
if !autoJoin && doublePuppet != nil {
|
|
err = doublePuppet.EnsureJoined(ctx, ul.SpaceRoom)
|
|
if err != nil {
|
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to auto-join created space room with double puppet")
|
|
}
|
|
}
|
|
err = ul.Save(ctx)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to save space room ID: %w", err)
|
|
}
|
|
return ul.SpaceRoom, nil
|
|
}
|