forked from MirrorHub/mautrix-whatsapp
Add support for communities
This commit is contained in:
parent
357f165581
commit
a1192bd0a4
10 changed files with 182 additions and 31 deletions
|
@ -3,6 +3,7 @@
|
||||||
* Added support for bridging polls from WhatsApp and votes in both directions.
|
* Added support for bridging polls from WhatsApp and votes in both directions.
|
||||||
* Votes are only bridged if MSC3381 polls are enabled
|
* Votes are only bridged if MSC3381 polls are enabled
|
||||||
(`extev_polls` in the config).
|
(`extev_polls` in the config).
|
||||||
|
* Added support for bridging WhatsApp communities as spaces.
|
||||||
* Updated backfill logic to mark rooms as read if the only message is a notice
|
* Updated backfill logic to mark rooms as read if the only message is a notice
|
||||||
about the disappearing message timer.
|
about the disappearing message timer.
|
||||||
* Switched SQLite config from `sqlite3` to `sqlite3-fk-wal` to enforce foreign
|
* Switched SQLite config from `sqlite3` to `sqlite3-fk-wal` to enforce foreign
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
* [ ] Chat types
|
* [ ] Chat types
|
||||||
* [x] Private chat
|
* [x] Private chat
|
||||||
* [x] Group chat
|
* [x] Group chat
|
||||||
|
* [x] Communities
|
||||||
* [x] Status broadcast
|
* [x] Status broadcast
|
||||||
* [ ] Broadcast list (not currently supported on WhatsApp web)
|
* [ ] Broadcast list (not currently supported on WhatsApp web)
|
||||||
* [x] Message deletions
|
* [x] Message deletions
|
||||||
|
|
19
commands.go
19
commands.go
|
@ -356,6 +356,13 @@ func fnCreate(ce *WrappedCommandEvent) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var createEvent event.CreateEventContent
|
||||||
|
err = ce.Bot.StateEvent(ce.RoomID, event.StateCreate, "", &createEvent)
|
||||||
|
if err != nil && !errors.Is(err, mautrix.MNotFound) {
|
||||||
|
ce.Reply("Failed to get room create event")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var participants []types.JID
|
var participants []types.JID
|
||||||
participantDedup := make(map[types.JID]bool)
|
participantDedup := make(map[types.JID]bool)
|
||||||
participantDedup[ce.User.JID.ToNonAD()] = true
|
participantDedup[ce.User.JID.ToNonAD()] = true
|
||||||
|
@ -373,9 +380,16 @@ func fnCreate(ce *WrappedCommandEvent) {
|
||||||
participants = append(participants, jid)
|
participants = append(participants, jid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO check m.space.parent to create rooms directly in communities
|
||||||
|
|
||||||
ce.Log.Infofln("Creating group for %s with name %s and participants %+v", ce.RoomID, roomNameEvent.Name, participants)
|
ce.Log.Infofln("Creating group for %s with name %s and participants %+v", ce.RoomID, roomNameEvent.Name, participants)
|
||||||
resp, err := ce.User.Client.CreateGroup(roomNameEvent.Name, participants, "")
|
resp, err := ce.User.Client.CreateGroup(whatsmeow.ReqCreateGroup{
|
||||||
|
Name: roomNameEvent.Name,
|
||||||
|
Participants: participants,
|
||||||
|
GroupParent: types.GroupParent{
|
||||||
|
IsParent: createEvent.Type == event.RoomTypeSpace,
|
||||||
|
},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ce.Reply("Failed to create group: %v", err)
|
ce.Reply("Failed to create group: %v", err)
|
||||||
return
|
return
|
||||||
|
@ -389,6 +403,7 @@ func fnCreate(ce *WrappedCommandEvent) {
|
||||||
}
|
}
|
||||||
portal.MXID = ce.RoomID
|
portal.MXID = ce.RoomID
|
||||||
portal.Name = roomNameEvent.Name
|
portal.Name = roomNameEvent.Name
|
||||||
|
portal.IsParent = resp.IsParent
|
||||||
portal.Encrypted = encryptionEvent.Algorithm == id.AlgorithmMegolmV1
|
portal.Encrypted = encryptionEvent.Algorithm == id.AlgorithmMegolmV1
|
||||||
if !portal.Encrypted && ce.Bridge.Config.Bridge.Encryption.Default {
|
if !portal.Encrypted && ce.Bridge.Config.Bridge.Encryption.Default {
|
||||||
_, err = portal.MainIntent().SendStateEvent(portal.MXID, event.StateEncryption, "", portal.GetEncryptionEventContent())
|
_, err = portal.MainIntent().SendStateEvent(portal.MXID, event.StateEncryption, "", portal.GetEncryptionEventContent())
|
||||||
|
@ -1127,7 +1142,7 @@ func fnSync(ce *WrappedCommandEvent) {
|
||||||
count := 0
|
count := 0
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
portal := ce.Bridge.GetPortalByJID(key)
|
portal := ce.Bridge.GetPortalByJID(key)
|
||||||
portal.addToSpace(ce.User)
|
portal.addToPersonalSpace(ce.User)
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
plural := "s"
|
plural := "s"
|
||||||
|
|
|
@ -65,7 +65,7 @@ func (pq *PortalQuery) New() *Portal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const portalColumns = "jid, receiver, mxid, name, name_set, topic, topic_set, avatar, avatar_url, avatar_set, encrypted, last_sync, first_event_id, next_batch_id, relay_user_id, expiration_time"
|
const portalColumns = "jid, receiver, mxid, name, name_set, topic, topic_set, avatar, avatar_url, avatar_set, encrypted, last_sync, is_parent, parent_group, in_space, first_event_id, next_batch_id, relay_user_id, expiration_time"
|
||||||
|
|
||||||
func (pq *PortalQuery) GetAll() []*Portal {
|
func (pq *PortalQuery) GetAll() []*Portal {
|
||||||
return pq.getAll(fmt.Sprintf("SELECT %s FROM portal", portalColumns))
|
return pq.getAll(fmt.Sprintf("SELECT %s FROM portal", portalColumns))
|
||||||
|
@ -145,18 +145,20 @@ type Portal struct {
|
||||||
Encrypted bool
|
Encrypted bool
|
||||||
LastSync time.Time
|
LastSync time.Time
|
||||||
|
|
||||||
FirstEventID id.EventID
|
IsParent bool
|
||||||
NextBatchID id.BatchID
|
ParentGroup types.JID
|
||||||
|
InSpace bool
|
||||||
RelayUserID id.UserID
|
|
||||||
|
|
||||||
|
FirstEventID id.EventID
|
||||||
|
NextBatchID id.BatchID
|
||||||
|
RelayUserID id.UserID
|
||||||
ExpirationTime uint32
|
ExpirationTime uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) Scan(row dbutil.Scannable) *Portal {
|
func (portal *Portal) Scan(row dbutil.Scannable) *Portal {
|
||||||
var mxid, avatarURL, firstEventID, nextBatchID, relayUserID sql.NullString
|
var mxid, avatarURL, firstEventID, nextBatchID, relayUserID, parentGroupJID sql.NullString
|
||||||
var lastSyncTs int64
|
var lastSyncTs int64
|
||||||
err := row.Scan(&portal.Key.JID, &portal.Key.Receiver, &mxid, &portal.Name, &portal.NameSet, &portal.Topic, &portal.TopicSet, &portal.Avatar, &avatarURL, &portal.AvatarSet, &portal.Encrypted, &lastSyncTs, &firstEventID, &nextBatchID, &relayUserID, &portal.ExpirationTime)
|
err := row.Scan(&portal.Key.JID, &portal.Key.Receiver, &mxid, &portal.Name, &portal.NameSet, &portal.Topic, &portal.TopicSet, &portal.Avatar, &avatarURL, &portal.AvatarSet, &portal.Encrypted, &lastSyncTs, &portal.IsParent, &parentGroupJID, &portal.InSpace, &firstEventID, &nextBatchID, &relayUserID, &portal.ExpirationTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != sql.ErrNoRows {
|
if err != sql.ErrNoRows {
|
||||||
portal.log.Errorln("Database scan failed:", err)
|
portal.log.Errorln("Database scan failed:", err)
|
||||||
|
@ -168,6 +170,9 @@ func (portal *Portal) Scan(row dbutil.Scannable) *Portal {
|
||||||
}
|
}
|
||||||
portal.MXID = id.RoomID(mxid.String)
|
portal.MXID = id.RoomID(mxid.String)
|
||||||
portal.AvatarURL, _ = id.ParseContentURI(avatarURL.String)
|
portal.AvatarURL, _ = id.ParseContentURI(avatarURL.String)
|
||||||
|
if parentGroupJID.Valid {
|
||||||
|
portal.ParentGroup, _ = types.ParseJID(parentGroupJID.String)
|
||||||
|
}
|
||||||
portal.FirstEventID = id.EventID(firstEventID.String)
|
portal.FirstEventID = id.EventID(firstEventID.String)
|
||||||
portal.NextBatchID = id.BatchID(nextBatchID.String)
|
portal.NextBatchID = id.BatchID(nextBatchID.String)
|
||||||
portal.RelayUserID = id.UserID(relayUserID.String)
|
portal.RelayUserID = id.UserID(relayUserID.String)
|
||||||
|
@ -188,6 +193,14 @@ func (portal *Portal) relayUserPtr() *id.UserID {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (portal *Portal) parentGroupPtr() *string {
|
||||||
|
if !portal.ParentGroup.IsEmpty() {
|
||||||
|
val := portal.ParentGroup.String()
|
||||||
|
return &val
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (portal *Portal) lastSyncTs() int64 {
|
func (portal *Portal) lastSyncTs() int64 {
|
||||||
if portal.LastSync.IsZero() {
|
if portal.LastSync.IsZero() {
|
||||||
return 0
|
return 0
|
||||||
|
@ -198,12 +211,14 @@ func (portal *Portal) lastSyncTs() int64 {
|
||||||
func (portal *Portal) Insert() {
|
func (portal *Portal) Insert() {
|
||||||
_, err := portal.db.Exec(`
|
_, err := portal.db.Exec(`
|
||||||
INSERT INTO portal (jid, receiver, mxid, name, name_set, topic, topic_set, avatar, avatar_url, avatar_set,
|
INSERT INTO portal (jid, receiver, mxid, name, name_set, topic, topic_set, avatar, avatar_url, avatar_set,
|
||||||
encrypted, last_sync, first_event_id, next_batch_id, relay_user_id, expiration_time)
|
encrypted, last_sync, is_parent, parent_group, in_space, first_event_id, next_batch_id,
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
relay_user_id, expiration_time)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)
|
||||||
`,
|
`,
|
||||||
portal.Key.JID, portal.Key.Receiver, portal.mxidPtr(), portal.Name, portal.NameSet, portal.Topic, portal.TopicSet,
|
portal.Key.JID, portal.Key.Receiver, portal.mxidPtr(), portal.Name, portal.NameSet, portal.Topic, portal.TopicSet,
|
||||||
portal.Avatar, portal.AvatarURL.String(), portal.AvatarSet, portal.Encrypted, portal.lastSyncTs(),
|
portal.Avatar, portal.AvatarURL.String(), portal.AvatarSet, portal.Encrypted, portal.lastSyncTs(),
|
||||||
portal.FirstEventID.String(), portal.NextBatchID.String(), portal.relayUserPtr(), portal.ExpirationTime)
|
portal.IsParent, portal.parentGroupPtr(), portal.InSpace, portal.FirstEventID.String(), portal.NextBatchID.String(),
|
||||||
|
portal.relayUserPtr(), portal.ExpirationTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
portal.log.Warnfln("Failed to insert %s: %v", portal.Key, err)
|
portal.log.Warnfln("Failed to insert %s: %v", portal.Key, err)
|
||||||
}
|
}
|
||||||
|
@ -216,11 +231,13 @@ func (portal *Portal) Update(txn dbutil.Execable) {
|
||||||
_, err := txn.Exec(`
|
_, err := txn.Exec(`
|
||||||
UPDATE portal
|
UPDATE portal
|
||||||
SET mxid=$1, name=$2, name_set=$3, topic=$4, topic_set=$5, avatar=$6, avatar_url=$7, avatar_set=$8,
|
SET mxid=$1, name=$2, name_set=$3, topic=$4, topic_set=$5, avatar=$6, avatar_url=$7, avatar_set=$8,
|
||||||
encrypted=$9, last_sync=$10, first_event_id=$11, next_batch_id=$12, relay_user_id=$13, expiration_time=$14
|
encrypted=$9, last_sync=$10, is_parent=$11, parent_group=$12, in_space=$13,
|
||||||
WHERE jid=$15 AND receiver=$16
|
first_event_id=$14, next_batch_id=$15, relay_user_id=$16, expiration_time=$17
|
||||||
|
WHERE jid=$18 AND receiver=$19
|
||||||
`, portal.mxidPtr(), portal.Name, portal.NameSet, portal.Topic, portal.TopicSet, portal.Avatar, portal.AvatarURL.String(),
|
`, portal.mxidPtr(), portal.Name, portal.NameSet, portal.Topic, portal.TopicSet, portal.Avatar, portal.AvatarURL.String(),
|
||||||
portal.AvatarSet, portal.Encrypted, portal.lastSyncTs(), portal.FirstEventID.String(), portal.NextBatchID.String(),
|
portal.AvatarSet, portal.Encrypted, portal.lastSyncTs(), portal.IsParent, portal.parentGroupPtr(), portal.InSpace,
|
||||||
portal.relayUserPtr(), portal.ExpirationTime, portal.Key.JID, portal.Key.Receiver)
|
portal.FirstEventID.String(), portal.NextBatchID.String(), portal.relayUserPtr(), portal.ExpirationTime,
|
||||||
|
portal.Key.JID, portal.Key.Receiver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
portal.log.Warnfln("Failed to update %s: %v", portal.Key, err)
|
portal.log.Warnfln("Failed to update %s: %v", portal.Key, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
-- v0 -> v51: Latest revision
|
-- v0 -> v52: Latest revision
|
||||||
|
|
||||||
CREATE TABLE "user" (
|
CREATE TABLE "user" (
|
||||||
mxid TEXT PRIMARY KEY,
|
mxid TEXT PRIMARY KEY,
|
||||||
|
@ -29,6 +29,10 @@ CREATE TABLE portal (
|
||||||
encrypted BOOLEAN NOT NULL DEFAULT false,
|
encrypted BOOLEAN NOT NULL DEFAULT false,
|
||||||
last_sync BIGINT NOT NULL DEFAULT 0,
|
last_sync BIGINT NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
is_parent BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
parent_group TEXT,
|
||||||
|
in_space BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
first_event_id TEXT,
|
first_event_id TEXT,
|
||||||
next_batch_id TEXT,
|
next_batch_id TEXT,
|
||||||
relay_user_id TEXT,
|
relay_user_id TEXT,
|
||||||
|
|
5
database/upgrades/52-communities.sql
Normal file
5
database/upgrades/52-communities.sql
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
-- v52: Store portal metadata for communities
|
||||||
|
|
||||||
|
ALTER TABLE portal ADD COLUMN is_parent BOOLEAN NOT NULL DEFAULT false;
|
||||||
|
ALTER TABLE portal ADD COLUMN parent_group TEXT;
|
||||||
|
ALTER TABLE portal ADD COLUMN in_space BOOLEAN NOT NULL DEFAULT false;
|
6
go.mod
6
go.mod
|
@ -10,13 +10,13 @@ require (
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
github.com/prometheus/client_golang v1.14.0
|
github.com/prometheus/client_golang v1.14.0
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
github.com/tidwall/gjson v1.14.3
|
github.com/tidwall/gjson v1.14.4
|
||||||
go.mau.fi/whatsmeow v0.0.0-20221126173344-e660988acdbc
|
go.mau.fi/whatsmeow v0.0.0-20221202110551-e067ee7293b0
|
||||||
golang.org/x/image v0.1.0
|
golang.org/x/image v0.1.0
|
||||||
golang.org/x/net v0.2.0
|
golang.org/x/net v0.2.0
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.1
|
||||||
maunium.net/go/maulogger/v2 v2.3.2
|
maunium.net/go/maulogger/v2 v2.3.2
|
||||||
maunium.net/go/mautrix v0.12.4-0.20221122192554-26c9ef6e7157
|
maunium.net/go/mautrix v0.12.4-0.20221201124911-2c57226ad4cd
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
12
go.sum
12
go.sum
|
@ -53,8 +53,8 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
||||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
|
@ -66,8 +66,8 @@ github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
|
||||||
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.mau.fi/libsignal v0.0.0-20221015105917-d970e7c3c9cf h1:mzPxXBgDPHKDHMVV1tIWh7lwCiRpzCsXC0gNRX+K07c=
|
go.mau.fi/libsignal v0.0.0-20221015105917-d970e7c3c9cf h1:mzPxXBgDPHKDHMVV1tIWh7lwCiRpzCsXC0gNRX+K07c=
|
||||||
go.mau.fi/libsignal v0.0.0-20221015105917-d970e7c3c9cf/go.mod h1:XCjaU93vl71YNRPn059jMrK0xRDwVO5gKbxoPxow9mQ=
|
go.mau.fi/libsignal v0.0.0-20221015105917-d970e7c3c9cf/go.mod h1:XCjaU93vl71YNRPn059jMrK0xRDwVO5gKbxoPxow9mQ=
|
||||||
go.mau.fi/whatsmeow v0.0.0-20221126173344-e660988acdbc h1:uZCZs8Ju83OmM1A1+VhpZMXpvVAg5BEQNP0KBXALJBI=
|
go.mau.fi/whatsmeow v0.0.0-20221202110551-e067ee7293b0 h1:danzDOlj/KiDi8kNsaHOhwJ7IZdo7V7hXelkZXhJhsc=
|
||||||
go.mau.fi/whatsmeow v0.0.0-20221126173344-e660988acdbc/go.mod h1:2yweL8nczvtlIxkrvCb0y8xiO13rveX9lJPambwYV/E=
|
go.mau.fi/whatsmeow v0.0.0-20221202110551-e067ee7293b0/go.mod h1:2yweL8nczvtlIxkrvCb0y8xiO13rveX9lJPambwYV/E=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE=
|
golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE=
|
||||||
|
@ -122,5 +122,5 @@ maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
||||||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||||
maunium.net/go/maulogger/v2 v2.3.2 h1:1XmIYmMd3PoQfp9J+PaHhpt80zpfmMqaShzUTC7FwY0=
|
maunium.net/go/maulogger/v2 v2.3.2 h1:1XmIYmMd3PoQfp9J+PaHhpt80zpfmMqaShzUTC7FwY0=
|
||||||
maunium.net/go/maulogger/v2 v2.3.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
|
maunium.net/go/maulogger/v2 v2.3.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
|
||||||
maunium.net/go/mautrix v0.12.4-0.20221122192554-26c9ef6e7157 h1:x2SiQnZQeJJ8qYCwEJ/rN0SEpGgT/Ct8kqjB4y/7vM4=
|
maunium.net/go/mautrix v0.12.4-0.20221201124911-2c57226ad4cd h1:RHe8UuNE3opwiwvj4gRN7o5RYQYy9Gg8IsHxV218ms0=
|
||||||
maunium.net/go/mautrix v0.12.4-0.20221122192554-26c9ef6e7157/go.mod h1:uOUjkOjm2C+nQS3mr9B5ATjqemZfnPHvjdd1kZezAwg=
|
maunium.net/go/mautrix v0.12.4-0.20221201124911-2c57226ad4cd/go.mod h1:uOUjkOjm2C+nQS3mr9B5ATjqemZfnPHvjdd1kZezAwg=
|
||||||
|
|
110
portal.go
110
portal.go
|
@ -248,6 +248,7 @@ type Portal struct {
|
||||||
avatarLock sync.Mutex
|
avatarLock sync.Mutex
|
||||||
|
|
||||||
latestEventBackfillLock sync.Mutex
|
latestEventBackfillLock sync.Mutex
|
||||||
|
parentGroupUpdateLock sync.Mutex
|
||||||
|
|
||||||
recentlyHandled [recentlyHandledLength]recentlyHandledWrapper
|
recentlyHandled [recentlyHandledLength]recentlyHandledWrapper
|
||||||
recentlyHandledLock sync.Mutex
|
recentlyHandledLock sync.Mutex
|
||||||
|
@ -262,7 +263,8 @@ type Portal struct {
|
||||||
|
|
||||||
mediaErrorCache map[types.MessageID]*FailedMediaMeta
|
mediaErrorCache map[types.MessageID]*FailedMediaMeta
|
||||||
|
|
||||||
relayUser *User
|
relayUser *User
|
||||||
|
parentPortal *Portal
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -1194,6 +1196,27 @@ func (portal *Portal) UpdateTopic(topic string, setBy types.JID, updateInfo bool
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (portal *Portal) UpdateParentGroup(parent types.JID, updateInfo bool) bool {
|
||||||
|
portal.parentGroupUpdateLock.Lock()
|
||||||
|
defer portal.parentGroupUpdateLock.Unlock()
|
||||||
|
if portal.ParentGroup != parent {
|
||||||
|
portal.log.Debugfln("Updating parent group %v -> %v", portal.ParentGroup, parent)
|
||||||
|
portal.updateCommunitySpace(false)
|
||||||
|
portal.ParentGroup = parent
|
||||||
|
portal.parentPortal = nil
|
||||||
|
portal.InSpace = false
|
||||||
|
portal.updateCommunitySpace(true)
|
||||||
|
if updateInfo {
|
||||||
|
portal.UpdateBridgeInfo()
|
||||||
|
portal.Update(nil)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else if !portal.ParentGroup.IsEmpty() && !portal.InSpace {
|
||||||
|
return portal.updateCommunitySpace(true)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (portal *Portal) UpdateMetadata(user *User, groupInfo *types.GroupInfo) bool {
|
func (portal *Portal) UpdateMetadata(user *User, groupInfo *types.GroupInfo) bool {
|
||||||
if portal.IsPrivateChat() {
|
if portal.IsPrivateChat() {
|
||||||
return false
|
return false
|
||||||
|
@ -1230,10 +1253,18 @@ func (portal *Portal) UpdateMetadata(user *User, groupInfo *types.GroupInfo) boo
|
||||||
update := false
|
update := false
|
||||||
update = portal.UpdateName(groupInfo.Name, groupInfo.NameSetBy, false) || update
|
update = portal.UpdateName(groupInfo.Name, groupInfo.NameSetBy, false) || update
|
||||||
update = portal.UpdateTopic(groupInfo.Topic, groupInfo.TopicSetBy, false) || update
|
update = portal.UpdateTopic(groupInfo.Topic, groupInfo.TopicSetBy, false) || update
|
||||||
|
update = portal.UpdateParentGroup(groupInfo.LinkedParentJID, false) || update
|
||||||
if portal.ExpirationTime != groupInfo.DisappearingTimer {
|
if portal.ExpirationTime != groupInfo.DisappearingTimer {
|
||||||
update = true
|
update = true
|
||||||
portal.ExpirationTime = groupInfo.DisappearingTimer
|
portal.ExpirationTime = groupInfo.DisappearingTimer
|
||||||
}
|
}
|
||||||
|
if portal.IsParent != groupInfo.IsParent {
|
||||||
|
if portal.MXID != "" {
|
||||||
|
portal.log.Warnfln("Existing group changed is_parent from %t to %t", portal.IsParent, groupInfo.IsParent)
|
||||||
|
}
|
||||||
|
update = true
|
||||||
|
portal.IsParent = true
|
||||||
|
}
|
||||||
|
|
||||||
portal.RestrictMessageSending(groupInfo.IsAnnounce)
|
portal.RestrictMessageSending(groupInfo.IsAnnounce)
|
||||||
portal.RestrictMetadataChanges(groupInfo.IsLocked)
|
portal.RestrictMetadataChanges(groupInfo.IsLocked)
|
||||||
|
@ -1252,7 +1283,7 @@ func (portal *Portal) UpdateMatrixRoom(user *User, groupInfo *types.GroupInfo) b
|
||||||
portal.log.Infoln("Syncing portal for", user.MXID)
|
portal.log.Infoln("Syncing portal for", user.MXID)
|
||||||
|
|
||||||
portal.ensureUserInvited(user)
|
portal.ensureUserInvited(user)
|
||||||
go portal.addToSpace(user)
|
go portal.addToPersonalSpace(user)
|
||||||
|
|
||||||
update := false
|
update := false
|
||||||
update = portal.UpdateMetadata(user, groupInfo) || update
|
update = portal.UpdateMetadata(user, groupInfo) || update
|
||||||
|
@ -1401,6 +1432,13 @@ func (portal *Portal) getBridgeInfo() (string, event.BridgeEventContent) {
|
||||||
AvatarURL: portal.AvatarURL.CUString(),
|
AvatarURL: portal.AvatarURL.CUString(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if parent := portal.GetParentPortal(); parent != nil {
|
||||||
|
bridgeInfo.Network = &event.BridgeInfoSection{
|
||||||
|
ID: parent.Key.JID.String(),
|
||||||
|
DisplayName: parent.Name,
|
||||||
|
AvatarURL: parent.AvatarURL.CUString(),
|
||||||
|
}
|
||||||
|
}
|
||||||
return portal.getBridgeInfoStateKey(), bridgeInfo
|
return portal.getBridgeInfoStateKey(), bridgeInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1502,6 +1540,8 @@ func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo, i
|
||||||
if groupInfo != nil {
|
if groupInfo != nil {
|
||||||
portal.Name = groupInfo.Name
|
portal.Name = groupInfo.Name
|
||||||
portal.Topic = groupInfo.Topic
|
portal.Topic = groupInfo.Topic
|
||||||
|
portal.IsParent = groupInfo.IsParent
|
||||||
|
portal.ParentGroup = groupInfo.LinkedParentJID
|
||||||
}
|
}
|
||||||
portal.UpdateAvatar(user, types.EmptyJID, false)
|
portal.UpdateAvatar(user, types.EmptyJID, false)
|
||||||
}
|
}
|
||||||
|
@ -1552,6 +1592,19 @@ func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo, i
|
||||||
if !portal.bridge.Config.Bridge.FederateRooms {
|
if !portal.bridge.Config.Bridge.FederateRooms {
|
||||||
creationContent["m.federate"] = false
|
creationContent["m.federate"] = false
|
||||||
}
|
}
|
||||||
|
if portal.IsParent {
|
||||||
|
creationContent["type"] = event.RoomTypeSpace
|
||||||
|
} else if parent := portal.GetParentPortal(); parent != nil {
|
||||||
|
initialState = append(initialState, &event.Event{
|
||||||
|
Type: event.StateSpaceParent,
|
||||||
|
Content: event.Content{
|
||||||
|
Parsed: &event.SpaceParentEventContent{
|
||||||
|
Via: []string{portal.bridge.Config.Homeserver.Domain},
|
||||||
|
Canonical: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
autoJoinInvites := portal.bridge.Config.Homeserver.Software == bridgeconfig.SoftwareHungry
|
autoJoinInvites := portal.bridge.Config.Homeserver.Software == bridgeconfig.SoftwareHungry
|
||||||
if autoJoinInvites {
|
if autoJoinInvites {
|
||||||
portal.log.Debugfln("Hungryserv mode: adding all group members in create request")
|
portal.log.Debugfln("Hungryserv mode: adding all group members in create request")
|
||||||
|
@ -1582,14 +1635,16 @@ func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo, i
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
portal.log.Infoln("Matrix room created:", portal.MXID)
|
||||||
|
portal.InSpace = false
|
||||||
portal.NameSet = len(portal.Name) > 0
|
portal.NameSet = len(portal.Name) > 0
|
||||||
portal.TopicSet = len(portal.Topic) > 0
|
portal.TopicSet = len(portal.Topic) > 0
|
||||||
portal.MXID = resp.RoomID
|
portal.MXID = resp.RoomID
|
||||||
portal.bridge.portalsLock.Lock()
|
portal.bridge.portalsLock.Lock()
|
||||||
portal.bridge.portalsByMXID[portal.MXID] = portal
|
portal.bridge.portalsByMXID[portal.MXID] = portal
|
||||||
portal.bridge.portalsLock.Unlock()
|
portal.bridge.portalsLock.Unlock()
|
||||||
|
portal.updateCommunitySpace(true)
|
||||||
portal.Update(nil)
|
portal.Update(nil)
|
||||||
portal.log.Infoln("Matrix room created:", portal.MXID)
|
|
||||||
|
|
||||||
// We set the memberships beforehand to make sure the encryption key exchange in initial backfill knows the users are here.
|
// We set the memberships beforehand to make sure the encryption key exchange in initial backfill knows the users are here.
|
||||||
inviteMembership := event.MembershipInvite
|
inviteMembership := event.MembershipInvite
|
||||||
|
@ -1605,7 +1660,7 @@ func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo, i
|
||||||
}
|
}
|
||||||
user.syncChatDoublePuppetDetails(portal, true)
|
user.syncChatDoublePuppetDetails(portal, true)
|
||||||
|
|
||||||
go portal.addToSpace(user)
|
go portal.addToPersonalSpace(user)
|
||||||
|
|
||||||
if groupInfo != nil {
|
if groupInfo != nil {
|
||||||
if groupInfo.IsEphemeral {
|
if groupInfo.IsEphemeral {
|
||||||
|
@ -1655,7 +1710,7 @@ func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo, i
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (portal *Portal) addToSpace(user *User) {
|
func (portal *Portal) addToPersonalSpace(user *User) {
|
||||||
spaceID := user.GetSpaceRoom()
|
spaceID := user.GetSpaceRoom()
|
||||||
if len(spaceID) == 0 || user.IsInSpace(portal.Key) {
|
if len(spaceID) == 0 || user.IsInSpace(portal.Key) {
|
||||||
return
|
return
|
||||||
|
@ -1671,6 +1726,42 @@ func (portal *Portal) addToSpace(user *User) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (portal *Portal) updateCommunitySpace(add bool) bool {
|
||||||
|
if add == portal.InSpace {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
space := portal.GetParentPortal()
|
||||||
|
if space == nil || space.MXID == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var action string
|
||||||
|
var parentContent event.SpaceParentEventContent
|
||||||
|
var childContent event.SpaceChildEventContent
|
||||||
|
if add {
|
||||||
|
parentContent.Canonical = true
|
||||||
|
parentContent.Via = []string{portal.bridge.Config.Homeserver.Domain}
|
||||||
|
childContent.Via = []string{portal.bridge.Config.Homeserver.Domain}
|
||||||
|
action = "add portal to"
|
||||||
|
portal.log.Debugfln("Adding %s to space %s (%s)", portal.MXID, space.MXID, space.Key.JID)
|
||||||
|
} else {
|
||||||
|
action = "remove portal from"
|
||||||
|
portal.log.Debugfln("Removing %s from space %s (%s)", portal.MXID, space.MXID, space.Key.JID)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := space.MainIntent().SendStateEvent(space.MXID, event.StateSpaceChild, portal.MXID.String(), &childContent)
|
||||||
|
if err != nil {
|
||||||
|
portal.log.Errorfln("Failed to send m.space.child event to %s %s: %v", action, space.MXID, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, err = portal.MainIntent().SendStateEvent(portal.MXID, event.StateSpaceParent, space.MXID.String(), &parentContent)
|
||||||
|
if err != nil {
|
||||||
|
portal.log.Warnfln("Failed to send m.space.parent event to %s %s: %v", action, space.MXID, err)
|
||||||
|
}
|
||||||
|
portal.InSpace = add
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (portal *Portal) IsPrivateChat() bool {
|
func (portal *Portal) IsPrivateChat() bool {
|
||||||
return portal.Key.JID.Server == types.DefaultUserServer
|
return portal.Key.JID.Server == types.DefaultUserServer
|
||||||
}
|
}
|
||||||
|
@ -1700,6 +1791,15 @@ func (portal *Portal) GetRelayUser() *User {
|
||||||
return portal.relayUser
|
return portal.relayUser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (portal *Portal) GetParentPortal() *Portal {
|
||||||
|
if portal.ParentGroup.IsEmpty() {
|
||||||
|
return nil
|
||||||
|
} else if portal.parentPortal == nil {
|
||||||
|
portal.parentPortal = portal.bridge.GetPortalByJID(database.NewPortalKey(portal.ParentGroup, portal.ParentGroup))
|
||||||
|
}
|
||||||
|
return portal.parentPortal
|
||||||
|
}
|
||||||
|
|
||||||
func (portal *Portal) MainIntent() *appservice.IntentAPI {
|
func (portal *Portal) MainIntent() *appservice.IntentAPI {
|
||||||
if portal.IsPrivateChat() {
|
if portal.IsPrivateChat() {
|
||||||
return portal.bridge.GetPuppetByJID(portal.Key.JID).DefaultIntent()
|
return portal.bridge.GetPuppetByJID(portal.Key.JID).DefaultIntent()
|
||||||
|
|
8
user.go
8
user.go
|
@ -1322,6 +1322,14 @@ func (user *User) handleGroupUpdate(evt *events.GroupInfo) {
|
||||||
portal.ChangeAdminStatus(evt.Demote, false)
|
portal.ChangeAdminStatus(evt.Demote, false)
|
||||||
case evt.Ephemeral != nil:
|
case evt.Ephemeral != nil:
|
||||||
portal.UpdateGroupDisappearingMessages(evt.Sender, evt.Timestamp, evt.Ephemeral.DisappearingTimer)
|
portal.UpdateGroupDisappearingMessages(evt.Sender, evt.Timestamp, evt.Ephemeral.DisappearingTimer)
|
||||||
|
case evt.Link != nil:
|
||||||
|
if evt.Link.Type == types.GroupLinkChangeTypeParent {
|
||||||
|
portal.UpdateParentGroup(evt.Link.Group.JID, true)
|
||||||
|
}
|
||||||
|
case evt.Unlink != nil:
|
||||||
|
if evt.Unlink.Type == types.GroupLinkChangeTypeParent && portal.ParentGroup == evt.Unlink.Group.JID {
|
||||||
|
portal.UpdateParentGroup(types.EmptyJID, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue