Store phone settings for users

This commit is contained in:
Tulir Asokan 2023-08-30 20:45:14 +03:00
parent 48d761a397
commit 1618051a12
7 changed files with 50 additions and 15 deletions

View file

@ -1,4 +1,4 @@
-- v0 -> v5: Latest revision -- v0 -> v6: Latest revision
CREATE TABLE "user" ( CREATE TABLE "user" (
-- only: postgres -- only: postgres
@ -12,6 +12,7 @@ CREATE TABLE "user" (
self_participant_ids jsonb NOT NULL DEFAULT '[]', self_participant_ids jsonb NOT NULL DEFAULT '[]',
sim_metadata jsonb NOT NULL DEFAULT '{}', sim_metadata jsonb NOT NULL DEFAULT '{}',
settings jsonb NOT NULL DEFAULT '{}',
management_room TEXT, management_room TEXT,
space_room TEXT, space_room TEXT,

View file

@ -0,0 +1,2 @@
-- v6: Store phone settings for users
ALTER TABLE "user" ADD COLUMN settings jsonb NOT NULL DEFAULT '{}';

View file

@ -47,19 +47,26 @@ func (uq *UserQuery) getDB() *Database {
} }
func (uq *UserQuery) GetAllWithSession(ctx context.Context) ([]*User, error) { func (uq *UserQuery) GetAllWithSession(ctx context.Context) ([]*User, error) {
return getAll[*User](uq, ctx, `SELECT rowid, mxid, phone_id, session, self_participant_ids, sim_metadata, management_room, space_room, access_token FROM "user" WHERE session IS NOT NULL`) return getAll[*User](uq, ctx, `SELECT rowid, mxid, phone_id, session, self_participant_ids, sim_metadata, settings, management_room, space_room, access_token FROM "user" WHERE session IS NOT NULL`)
} }
func (uq *UserQuery) GetAllWithDoublePuppet(ctx context.Context) ([]*User, error) { func (uq *UserQuery) GetAllWithDoublePuppet(ctx context.Context) ([]*User, error) {
return getAll[*User](uq, ctx, `SELECT rowid, mxid, phone_id, session, self_participant_ids, sim_metadata, management_room, space_room, access_token FROM "user" WHERE access_token<>''`) return getAll[*User](uq, ctx, `SELECT rowid, mxid, phone_id, session, self_participant_ids, sim_metadata, settings, management_room, space_room, access_token FROM "user" WHERE access_token<>''`)
} }
func (uq *UserQuery) GetByRowID(ctx context.Context, rowID int) (*User, error) { func (uq *UserQuery) GetByRowID(ctx context.Context, rowID int) (*User, error) {
return get[*User](uq, ctx, `SELECT rowid, mxid, phone_id, session, self_participant_ids, sim_metadata, management_room, space_room, access_token FROM "user" WHERE rowid=$1`, rowID) return get[*User](uq, ctx, `SELECT rowid, mxid, phone_id, session, self_participant_ids, sim_metadata, settings, management_room, space_room, access_token FROM "user" WHERE rowid=$1`, rowID)
} }
func (uq *UserQuery) GetByMXID(ctx context.Context, userID id.UserID) (*User, error) { func (uq *UserQuery) GetByMXID(ctx context.Context, userID id.UserID) (*User, error) {
return get[*User](uq, ctx, `SELECT rowid, mxid, phone_id, session, self_participant_ids, sim_metadata, management_room, space_room, access_token FROM "user" WHERE mxid=$1`, userID) return get[*User](uq, ctx, `SELECT rowid, mxid, phone_id, session, self_participant_ids, sim_metadata, settings, management_room, space_room, access_token FROM "user" WHERE mxid=$1`, userID)
}
type Settings struct {
RCSEnabled bool `json:"rcs_enabled"`
ReadReceipts bool `json:"read_receipts"`
TypingNotifications bool `json:"typing_notifications"`
IsDefaultSMSApp bool `json:"is_default_sms_app"`
} }
type User struct { type User struct {
@ -79,13 +86,15 @@ type User struct {
simMetadata map[string]*gmproto.SIMCard simMetadata map[string]*gmproto.SIMCard
simMetadataLock sync.RWMutex simMetadataLock sync.RWMutex
Settings Settings
AccessToken string AccessToken string
} }
func (user *User) Scan(row dbutil.Scannable) (*User, error) { func (user *User) Scan(row dbutil.Scannable) (*User, error) {
var phoneID, session, managementRoom, spaceRoom, accessToken sql.NullString var phoneID, session, managementRoom, spaceRoom, accessToken sql.NullString
var selfParticipantIDs, simMetadata string var selfParticipantIDs, simMetadata, settings string
err := row.Scan(&user.RowID, &user.MXID, &phoneID, &session, &selfParticipantIDs, &simMetadata, &managementRoom, &spaceRoom, &accessToken) err := row.Scan(&user.RowID, &user.MXID, &phoneID, &session, &selfParticipantIDs, &simMetadata, &settings, &managementRoom, &spaceRoom, &accessToken)
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
return nil, nil return nil, nil
} else if err != nil { } else if err != nil {
@ -111,6 +120,10 @@ func (user *User) Scan(row dbutil.Scannable) (*User, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse SIM metadata: %w", err) return nil, fmt.Errorf("failed to parse SIM metadata: %w", err)
} }
err = json.Unmarshal([]byte(settings), &user.Settings)
if err != nil {
return nil, fmt.Errorf("failed to parse settings: %w", err)
}
user.PhoneID = phoneID.String user.PhoneID = phoneID.String
user.AccessToken = accessToken.String user.AccessToken = accessToken.String
user.ManagementRoom = id.RoomID(managementRoom.String) user.ManagementRoom = id.RoomID(managementRoom.String)
@ -146,7 +159,11 @@ func (user *User) sqlVariables() []any {
panic(err) panic(err)
} }
user.simMetadataLock.RUnlock() user.simMetadataLock.RUnlock()
return []any{user.MXID, phoneID, session, string(selfParticipantIDs), string(simMetadata), managementRoom, spaceRoom, accessToken} settings, err := json.Marshal(&user.Settings)
if err != nil {
panic(err)
}
return []any{user.MXID, phoneID, session, string(selfParticipantIDs), string(simMetadata), string(settings), managementRoom, spaceRoom, accessToken}
} }
func (user *User) IsSelfParticipantID(id string) bool { func (user *User) IsSelfParticipantID(id string) bool {
@ -233,12 +250,12 @@ func (user *User) AddSelfParticipantID(ctx context.Context, id string) error {
func (user *User) Insert(ctx context.Context) error { func (user *User) Insert(ctx context.Context) error {
err := user.db.Conn(ctx). err := user.db.Conn(ctx).
QueryRowContext(ctx, `INSERT INTO "user" (mxid, phone_id, session, self_participant_ids, sim_metadata, management_room, space_room, access_token) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING rowid`, user.sqlVariables()...). QueryRowContext(ctx, `INSERT INTO "user" (mxid, phone_id, session, self_participant_ids, sim_metadata, settings, management_room, space_room, access_token) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING rowid`, user.sqlVariables()...).
Scan(&user.RowID) Scan(&user.RowID)
return err return err
} }
func (user *User) Update(ctx context.Context) error { func (user *User) Update(ctx context.Context) error {
_, err := user.db.Conn(ctx).ExecContext(ctx, `UPDATE "user" SET phone_id=$2, session=$3, self_participant_ids=$4, sim_metadata=$5, management_room=$6, space_room=$7, access_token=$8 WHERE mxid=$1`, user.sqlVariables()...) _, err := user.db.Conn(ctx).ExecContext(ctx, `UPDATE "user" SET phone_id=$2, session=$3, self_participant_ids=$4, sim_metadata=$5, settings=$6, management_room=$7, space_room=$8, access_token=$9 WHERE mxid=$1`, user.sqlVariables()...)
return err return err
} }

View file

@ -613,7 +613,7 @@ type RCSSettings struct {
IsEnabled bool `protobuf:"varint,1,opt,name=isEnabled,proto3" json:"isEnabled,omitempty"` IsEnabled bool `protobuf:"varint,1,opt,name=isEnabled,proto3" json:"isEnabled,omitempty"`
SendReadReceipts bool `protobuf:"varint,2,opt,name=sendReadReceipts,proto3" json:"sendReadReceipts,omitempty"` SendReadReceipts bool `protobuf:"varint,2,opt,name=sendReadReceipts,proto3" json:"sendReadReceipts,omitempty"`
ShowTypingIndicators bool `protobuf:"varint,3,opt,name=showTypingIndicators,proto3" json:"showTypingIndicators,omitempty"` ShowTypingIndicators bool `protobuf:"varint,3,opt,name=showTypingIndicators,proto3" json:"showTypingIndicators,omitempty"`
Bool4 bool `protobuf:"varint,4,opt,name=bool4,proto3" json:"bool4,omitempty"` IsDefaultSMSApp bool `protobuf:"varint,4,opt,name=isDefaultSMSApp,proto3" json:"isDefaultSMSApp,omitempty"` // uncertain, but this field seems to disappear when gmessages is un-defaulted
} }
func (x *RCSSettings) Reset() { func (x *RCSSettings) Reset() {
@ -669,9 +669,9 @@ func (x *RCSSettings) GetShowTypingIndicators() bool {
return false return false
} }
func (x *RCSSettings) GetBool4() bool { func (x *RCSSettings) GetIsDefaultSMSApp() bool {
if x != nil { if x != nil {
return x.Bool4 return x.IsDefaultSMSApp
} }
return false return false
} }

Binary file not shown.

View file

@ -64,7 +64,7 @@ message RCSSettings {
bool isEnabled = 1; bool isEnabled = 1;
bool sendReadReceipts = 2; bool sendReadReceipts = 2;
bool showTypingIndicators = 3; bool showTypingIndicators = 3;
bool bool4 = 4; bool isDefaultSMSApp = 4; // uncertain, but this field seems to disappear when gmessages is un-defaulted
} }
message BooleanFields2 { message BooleanFields2 {

17
user.go
View file

@ -742,7 +742,21 @@ func (user *User) handleSettings(settings *gmproto.Settings) {
return return
} }
ctx := context.TODO() ctx := context.TODO()
if user.SetSIMs(settings.SIMCards) { changed := user.SetSIMs(settings.SIMCards)
newRCSSettings := settings.GetRCSSettings()
if user.Settings.RCSEnabled != newRCSSettings.GetIsEnabled() ||
user.Settings.ReadReceipts != newRCSSettings.GetSendReadReceipts() ||
user.Settings.TypingNotifications != newRCSSettings.GetShowTypingIndicators() ||
user.Settings.IsDefaultSMSApp != newRCSSettings.GetIsDefaultSMSApp() {
user.Settings = database.Settings{
RCSEnabled: newRCSSettings.GetIsEnabled(),
ReadReceipts: newRCSSettings.GetSendReadReceipts(),
TypingNotifications: newRCSSettings.GetShowTypingIndicators(),
IsDefaultSMSApp: newRCSSettings.GetIsDefaultSMSApp(),
}
changed = true
}
if changed {
err := user.Update(ctx) err := user.Update(ctx)
if err != nil { if err != nil {
user.zlog.Err(err).Msg("Failed to save SIM details") user.zlog.Err(err).Msg("Failed to save SIM details")
@ -757,6 +771,7 @@ func (user *User) FillBridgeState(state status.BridgeState) status.BridgeState {
} }
if state.StateEvent == status.StateConnected { if state.StateEvent == status.StateConnected {
state.Info["sims"] = user.GetSIMsForBridgeState() state.Info["sims"] = user.GetSIMsForBridgeState()
state.Info["settings"] = user.Settings
state.Info["battery_low"] = user.batteryLow state.Info["battery_low"] = user.batteryLow
state.Info["mobile_data"] = user.mobileData state.Info["mobile_data"] = user.mobileData
state.Info["browser_active"] = user.browserInactiveType == "" state.Info["browser_active"] = user.browserInactiveType == ""