Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Organize bridge rooms into a space #413

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type BridgeConfig struct {
WhatsappThumbnail bool `yaml:"whatsapp_thumbnail"`
AllowUserInvite bool `yaml:"allow_user_invite"`
FederateRooms bool `yaml:"federate_rooms"`
SpacePerUser bool `yaml:"space_per_user"`

CommandPrefix string `yaml:"command_prefix"`

Expand Down
1 change: 1 addition & 0 deletions config/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func (helper *UpgradeHelper) doUpgrade() {
helper.Copy(Bool, "bridge", "allow_user_invite")
helper.Copy(Str, "bridge", "command_prefix")
helper.Copy(Bool, "bridge", "federate_rooms")
helper.Copy(Bool, "bridge", "space_per_user")
helper.Copy(Str, "bridge", "management_room_text", "welcome")
helper.Copy(Str, "bridge", "management_room_text", "welcome_connected")
helper.Copy(Str, "bridge", "management_room_text", "welcome_unconnected")
Expand Down
12 changes: 12 additions & 0 deletions database/upgrades/2021-12-28-management-space.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package upgrades

import (
"database/sql"
)

func init() {
upgrades[32] = upgrade{"Store space in user table", func(tx *sql.Tx, ctx context) error {
_, err := tx.Exec(`ALTER TABLE "user" ADD COLUMN space_room TEXT NOT NULL DEFAULT ''`)
return err
}}
}
2 changes: 1 addition & 1 deletion database/upgrades/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type upgrade struct {
fn upgradeFunc
}

const NumberOfUpgrades = 33
const NumberOfUpgrades = 34

var upgrades [NumberOfUpgrades]upgrade

Expand Down
17 changes: 9 additions & 8 deletions database/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (uq *UserQuery) New() *User {
}

func (uq *UserQuery) GetAll() (users []*User) {
rows, err := uq.db.Query(`SELECT mxid, username, agent, device, management_room FROM "user"`)
rows, err := uq.db.Query(`SELECT mxid, username, agent, device, management_room, space_room FROM "user"`)
if err != nil || rows == nil {
return nil
}
Expand All @@ -50,15 +50,15 @@ func (uq *UserQuery) GetAll() (users []*User) {
}

func (uq *UserQuery) GetByMXID(userID id.UserID) *User {
row := uq.db.QueryRow(`SELECT mxid, username, agent, device, management_room FROM "user" WHERE mxid=$1`, userID)
row := uq.db.QueryRow(`SELECT mxid, username, agent, device, management_room, space_room FROM "user" WHERE mxid=$1`, userID)
if row == nil {
return nil
}
return uq.New().Scan(row)
}

func (uq *UserQuery) GetByUsername(username string) *User {
row := uq.db.QueryRow(`SELECT mxid, username, agent, device, management_room FROM "user" WHERE username=$1`, username)
row := uq.db.QueryRow(`SELECT mxid, username, agent, device, management_room, space_room FROM "user" WHERE username=$1`, username)
if row == nil {
return nil
}
Expand All @@ -72,12 +72,13 @@ type User struct {
MXID id.UserID
JID types.JID
ManagementRoom id.RoomID
SpaceRoom id.RoomID
}

func (user *User) Scan(row Scannable) *User {
var username sql.NullString
var device, agent sql.NullByte
err := row.Scan(&user.MXID, &username, &agent, &device, &user.ManagementRoom)
err := row.Scan(&user.MXID, &username, &agent, &device, &user.ManagementRoom, &user.SpaceRoom)
if err != nil {
if err != sql.ErrNoRows {
user.log.Errorln("Database scan failed:", err)
Expand Down Expand Up @@ -112,16 +113,16 @@ func (user *User) devicePtr() *uint8 {
}

func (user *User) Insert() {
_, err := user.db.Exec(`INSERT INTO "user" (mxid, username, agent, device, management_room) VALUES ($1, $2, $3, $4, $5)`,
user.MXID, user.usernamePtr(), user.agentPtr(), user.devicePtr(), user.ManagementRoom)
_, err := user.db.Exec(`INSERT INTO "user" (mxid, username, agent, device, management_room, space_room) VALUES ($1, $2, $3, $4, $5, $6)`,
user.MXID, user.usernamePtr(), user.agentPtr(), user.devicePtr(), user.ManagementRoom, user.SpaceRoom)
if err != nil {
user.log.Warnfln("Failed to insert %s: %v", user.MXID, err)
}
}

func (user *User) Update() {
_, err := user.db.Exec(`UPDATE "user" SET username=$1, agent=$2, device=$3, management_room=$4 WHERE mxid=$5`,
user.usernamePtr(), user.agentPtr(), user.devicePtr(), user.ManagementRoom, user.MXID)
_, err := user.db.Exec(`UPDATE "user" SET username=$1, agent=$2, device=$3, management_room=$4, space_room=$5 WHERE mxid=$6`,
user.usernamePtr(), user.agentPtr(), user.devicePtr(), user.ManagementRoom, user.SpaceRoom, user.MXID)
if err != nil {
user.log.Warnfln("Failed to update %s: %v", user.MXID, err)
}
Expand Down
4 changes: 4 additions & 0 deletions example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,14 @@ bridge:
# Whether or not created rooms should have federation enabled.
# If false, created portal rooms will never be federated.
federate_rooms: true
# Creates a space for all the rooms bridged
space_per_user: false

# The prefix for commands. Only required in non-management rooms.
command_prefix: "!wa"



# Messages sent upon joining a management room.
# Markdown is supported. The defaults are listed below.
management_room_text:
Expand Down
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ type Bridge struct {
usersByMXID map[id.UserID]*User
usersByUsername map[string]*User
usersLock sync.Mutex
spaceRooms map[id.RoomID]*User
spaceRoomsLock sync.Mutex
managementRooms map[id.RoomID]*User
HelderFSFerreira marked this conversation as resolved.
Show resolved Hide resolved
managementRoomsLock sync.Mutex
portalsByMXID map[id.RoomID]*Portal
Expand Down Expand Up @@ -479,6 +481,7 @@ func main() {
(&Bridge{
usersByMXID: make(map[id.UserID]*User),
usersByUsername: make(map[string]*User),
spaceRooms: make(map[id.RoomID]*User),
managementRooms: make(map[id.RoomID]*User),
portalsByMXID: make(map[id.RoomID]*Portal),
portalsByJID: make(map[database.PortalKey]*Portal),
Expand Down
28 changes: 28 additions & 0 deletions portal.go
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,21 @@ func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo, i
Content: event.Content{Parsed: bridgeInfo},
StateKey: &bridgeInfoStateKey,
}}

if portal.bridge.Config.Bridge.SpacePerUser {
spaceRoomID := user.getSpaceRoom().String()

parentSpaceContent := make(map[string]interface{})
parentSpaceContent["via"] = []string{portal.bridge.Config.Homeserver.Domain}
parentSpaceContent["canonical"] = true

initialState = append(initialState, &event.Event{
Type: event.Type{Type: "m.space.parent", Class: event.StateEventType},
Content: event.Content{Raw: parentSpaceContent},
StateKey: &spaceRoomID,
})
}

if !portal.AvatarURL.IsEmpty() {
initialState = append(initialState, &event.Event{
Type: event.StateRoomAvatar,
Expand Down Expand Up @@ -1141,6 +1156,10 @@ func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo, i
portal.ensureUserInvited(user)
user.syncChatDoublePuppetDetails(portal, true)

if portal.bridge.Config.Bridge.SpacePerUser {
portal.addToSpace(user.getSpaceRoom(), portal.MXID, portal.bridge.Config.Homeserver.Domain)
}

if groupInfo != nil {
portal.SyncParticipants(user, groupInfo)
if groupInfo.IsAnnounce {
Expand Down Expand Up @@ -1176,6 +1195,15 @@ func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo, i
return nil
}

func (portal *Portal) addToSpace(spaceID id.RoomID, portalID id.RoomID, homeserverDomain string) {
parentSpaceContent := make(map[string]interface{})
parentSpaceContent["via"] = []string{homeserverDomain}

portal.log.Debugfln("adding room %s to the space %s", portalID, spaceID)

portal.MainIntent().SendStateEvent(spaceID, event.Type{Type: "m.space.child", Class: event.StateEventType}, portalID.String(), parentSpaceContent)
}

func (portal *Portal) IsPrivateChat() bool {
return portal.Key.JID.Server == types.DefaultUserServer
}
Expand Down
55 changes: 53 additions & 2 deletions user.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ type User struct {
Whitelisted bool
RelayWhitelisted bool

mgmtCreateLock sync.Mutex
connLock sync.Mutex
mgmtCreateLock sync.Mutex
spaceCreateLock sync.Mutex
connLock sync.Mutex

historySyncs chan *events.HistorySync
prevBridgeStatus *BridgeState
Expand Down Expand Up @@ -179,6 +180,56 @@ func (bridge *Bridge) NewUser(dbUser *database.User) *User {
return user
}

func (user *User) getSpaceRoom() id.RoomID {
var roomID id.RoomID

if len(user.SpaceRoom) == 0 {
//Create Space
user.log.Debugln("Locking to create space.")
user.spaceCreateLock.Lock()
defer user.spaceCreateLock.Unlock()

if len(user.SpaceRoom) != 0 {
roomID = user.SpaceRoom
user.log.Debugln("Returning space after lock" + user.SpaceRoom)
} else {
creationContent := make(map[string]interface{})
creationContent["type"] = "m.space"

user.log.Debugln("Creating a new space for the user")

user.log.Debugln("Inviting user " + user.MXID)
var invite []id.UserID
invite = append(invite, user.MXID)

resp, err := user.bridge.Bot.CreateRoom(&mautrix.ReqCreateRoom{
Visibility: "private",
Name: "WhatsApp",
Topic: "WhatsApp bridge Space",
Invite: invite,
CreationContent: creationContent,
})
if err != nil {
user.log.Errorln("Failed to auto-create space room:", err)
} else {
user.setSpaceRoom(resp.RoomID)
roomID = resp.RoomID
}
}
} else {
user.log.Debugln("Space found" + user.SpaceRoom)
roomID = user.SpaceRoom
}

return roomID
}

func (user *User) setSpaceRoom(spaceID id.RoomID) {
user.SpaceRoom = spaceID
user.bridge.spaceRooms[user.SpaceRoom] = user
user.Update()
}

func (user *User) GetManagementRoom() id.RoomID {
if len(user.ManagementRoom) == 0 {
user.mgmtCreateLock.Lock()
Expand Down