diff --git a/database/upgrades/06-user-read-state-version.sql b/database/upgrades/06-user-read-state-version.sql new file mode 100644 index 0000000..612a777 --- /dev/null +++ b/database/upgrades/06-user-read-state-version.sql @@ -0,0 +1,2 @@ +-- v6: Store user read state version +ALTER TABLE "user" ADD COLUMN read_state_version INTEGER NOT NULL DEFAULT 0; diff --git a/database/user.go b/database/user.go index 6c05208..9b2cb00 100644 --- a/database/user.go +++ b/database/user.go @@ -22,18 +22,18 @@ func (uq *UserQuery) New() *User { } func (uq *UserQuery) GetByMXID(userID id.UserID) *User { - query := `SELECT mxid, dcid, discord_token, management_room, space_room, dm_space_room FROM "user" WHERE mxid=$1` + query := `SELECT mxid, dcid, discord_token, management_room, space_room, dm_space_room, read_state_version FROM "user" WHERE mxid=$1` return uq.New().Scan(uq.db.QueryRow(query, userID)) } func (uq *UserQuery) GetByID(id string) *User { - query := `SELECT mxid, dcid, discord_token, management_room, space_room, dm_space_room FROM "user" WHERE dcid=$1` + query := `SELECT mxid, dcid, discord_token, management_room, space_room, dm_space_room, read_state_version FROM "user" WHERE dcid=$1` return uq.New().Scan(uq.db.QueryRow(query, id)) } func (uq *UserQuery) GetAllWithToken() []*User { query := ` - SELECT mxid, dcid, discord_token, management_room, space_room, dm_space_room + SELECT mxid, dcid, discord_token, management_room, space_room, dm_space_room, read_state_version FROM "user" WHERE discord_token IS NOT NULL ` rows, err := uq.db.Query(query) @@ -61,11 +61,13 @@ type User struct { ManagementRoom id.RoomID SpaceRoom id.RoomID DMSpaceRoom id.RoomID + + ReadStateVersion int } func (u *User) Scan(row dbutil.Scannable) *User { var discordID, managementRoom, spaceRoom, dmSpaceRoom, discordToken sql.NullString - err := row.Scan(&u.MXID, &discordID, &discordToken, &managementRoom, &spaceRoom, &dmSpaceRoom) + err := row.Scan(&u.MXID, &discordID, &discordToken, &managementRoom, &spaceRoom, &dmSpaceRoom, &u.ReadStateVersion) if err != nil { if err != sql.ErrNoRows { u.log.Errorln("Database scan failed:", err) @@ -82,8 +84,8 @@ func (u *User) Scan(row dbutil.Scannable) *User { } func (u *User) Insert() { - query := `INSERT INTO "user" (mxid, dcid, discord_token, management_room, space_room, dm_space_room) VALUES ($1, $2, $3, $4, $5, $6)` - _, err := u.db.Exec(query, u.MXID, strPtr(u.DiscordID), strPtr(u.DiscordToken), strPtr(string(u.ManagementRoom)), strPtr(string(u.SpaceRoom)), strPtr(string(u.DMSpaceRoom))) + query := `INSERT INTO "user" (mxid, dcid, discord_token, management_room, space_room, dm_space_room, read_state_version) VALUES ($1, $2, $3, $4, $5, $6, $7)` + _, err := u.db.Exec(query, u.MXID, strPtr(u.DiscordID), strPtr(u.DiscordToken), strPtr(string(u.ManagementRoom)), strPtr(string(u.SpaceRoom)), strPtr(string(u.DMSpaceRoom)), u.ReadStateVersion) if err != nil { u.log.Warnfln("Failed to insert %s: %v", u.MXID, err) panic(err) @@ -91,8 +93,8 @@ func (u *User) Insert() { } func (u *User) Update() { - query := `UPDATE "user" SET dcid=$1, discord_token=$2, management_room=$3, space_room=$4, dm_space_room=$5 WHERE mxid=$6` - _, err := u.db.Exec(query, strPtr(u.DiscordID), strPtr(u.DiscordToken), strPtr(string(u.ManagementRoom)), strPtr(string(u.SpaceRoom)), strPtr(string(u.DMSpaceRoom)), u.MXID) + query := `UPDATE "user" SET dcid=$1, discord_token=$2, management_room=$3, space_room=$4, dm_space_room=$5, read_state_version=$6 WHERE mxid=$7` + _, err := u.db.Exec(query, strPtr(u.DiscordID), strPtr(u.DiscordToken), strPtr(string(u.ManagementRoom)), strPtr(string(u.SpaceRoom)), strPtr(string(u.DMSpaceRoom)), u.ReadStateVersion, u.MXID) if err != nil { u.log.Warnfln("Failed to update %q: %v", u.MXID, err) panic(err) diff --git a/go.mod b/go.mod index 5bf39ea..b419103 100644 --- a/go.mod +++ b/go.mod @@ -26,4 +26,4 @@ require ( maunium.net/go/mauflag v1.0.0 // indirect ) -replace github.com/bwmarrin/discordgo => gitlab.com/beeper/discordgo v0.23.3-0.20220708085215-5150e3de5797 +replace github.com/bwmarrin/discordgo => gitlab.com/beeper/discordgo v0.23.3-0.20220708095310-09da7ef6f6de diff --git a/go.sum b/go.sum index a464947..2b95fa0 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc= github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM= github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0= github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -gitlab.com/beeper/discordgo v0.23.3-0.20220708085215-5150e3de5797 h1:G+6sujr5CBD1+GyDq9Vobj/2XhaRejXzyOVDNNWlM/E= -gitlab.com/beeper/discordgo v0.23.3-0.20220708085215-5150e3de5797/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= +gitlab.com/beeper/discordgo v0.23.3-0.20220708095310-09da7ef6f6de h1:XSKsxfGXfUf7KTyzM1NmzYkqetqiLivUULhXr7alZX4= +gitlab.com/beeper/discordgo v0.23.3-0.20220708095310-09da7ef6f6de/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= diff --git a/user.go b/user.go index dd3a9cd..b5d8da7 100644 --- a/user.go +++ b/user.go @@ -129,37 +129,32 @@ func (user *User) GetIGhost() bridge.Ghost { var _ bridge.User = (*User)(nil) func (br *DiscordBridge) loadUser(dbUser *database.User, mxid *id.UserID) *User { - // If we weren't passed in a user we attempt to create one if we were given - // a matrix id. if dbUser == nil { if mxid == nil { return nil } - dbUser = br.DB.User.New() dbUser.MXID = *mxid dbUser.Insert() } user := br.NewUser(dbUser) - - // We assume the usersLock was acquired by our caller. br.usersByMXID[user.MXID] = user if user.DiscordID != "" { br.usersByID[user.DiscordID] = user } - if user.ManagementRoom != "" { - // Lock the management rooms for our update br.managementRoomsLock.Lock() br.managementRooms[user.ManagementRoom] = user br.managementRoomsLock.Unlock() } - return user } func (br *DiscordBridge) GetUserByMXID(userID id.UserID) *User { + if userID == br.Bot.UserID || br.IsGhost(userID) { + return nil + } br.usersLock.Lock() defer br.usersLock.Unlock() @@ -167,7 +162,6 @@ func (br *DiscordBridge) GetUserByMXID(userID id.UserID) *User { if !ok { return br.loadUser(br.DB.User.GetByMXID(userID), &userID) } - return user } @@ -179,7 +173,6 @@ func (br *DiscordBridge) GetUserByID(id string) *User { if !ok { return br.loadUser(br.DB.User.GetByID(id), nil) } - return user } @@ -192,7 +185,6 @@ func (br *DiscordBridge) NewUser(dbUser *database.User) *User { user.PermissionLevel = br.Config.Bridge.Permissions.Get(user.MXID) user.BridgeState = br.NewBridgeStateQueue(user, user.log) - return user } @@ -210,7 +202,6 @@ func (br *DiscordBridge) getAllUsersWithToken() []*User { } users[idx] = user } - return users } @@ -538,6 +529,19 @@ func (user *User) readyHandler(_ *discordgo.Session, r *discordgo.Ready) { portal := user.GetPortalByMeta(ch) user.handlePrivateChannel(portal, ch, updateTS, i < maxCreate, portalsInSpace[portal.Key.String()]) } + + if r.ReadState.Version > user.ReadStateVersion { + // TODO can we figure out which read states are actually new? + for _, entry := range r.ReadState.Entries { + user.messageAckHandler(nil, &discordgo.MessageAck{ + MessageID: string(entry.LastMessageID), + ChannelID: entry.ID, + }) + } + user.ReadStateVersion = r.ReadState.Version + user.Update() + } + user.BridgeState.Send(bridge.State{StateEvent: bridge.StateConnected}) } @@ -761,6 +765,11 @@ func (user *User) messageAckHandler(_ *discordgo.Session, m *discordgo.MessageAc user.log.Warnfln("Failed to mark %s/%s as read: %v", msg.MXID, msg.DiscordID, err) } else { user.log.Debugfln("Marked %s/%s as read after Discord message ack event", msg.MXID, msg.DiscordID) + if user.ReadStateVersion < m.Version { + user.ReadStateVersion = m.Version + // TODO maybe don't update every time? + user.Update() + } } }