diff --git a/commands.go b/commands.go index d2716e7..d35ab99 100644 --- a/commands.go +++ b/commands.go @@ -52,6 +52,8 @@ func (br *GMBridge) RegisterCommands() { cmdDisconnect, cmdSetActive, cmdPing, + cmdToggleBatteryNotifications, + cmdToggleVerboseNotifications, cmdPM, cmdDeletePortal, cmdDeleteAllPortals, @@ -402,6 +404,44 @@ func fnPing(ce *WrappedCommandEvent) { } } +var cmdToggleBatteryNotifications = &commands.FullHandler{ + Func: wrapCommand(fnToggleBatteryNotifications), + Name: "toggle-battery-notifications", + Help: commands.HelpMeta{ + Section: HelpSectionConnectionManagement, + Description: "Silence Battery statuses.", + }, +} + +func fnToggleBatteryNotifications(ce *WrappedCommandEvent) { + ce.User.toggleNotifyBattery() + if ce.User.DisableNotifyBattery { + ce.Reply("Disabled battery notifications") + } else { + ce.Reply("Enabled battery notifications") + } + ce.ZLog.Trace().Msg("ToggleBatteryNotifications command finished") +} + +var cmdToggleVerboseNotifications = &commands.FullHandler{ + Func: wrapCommand(fnToggleVerboseNotifications), + Name: "toggle-verbose-notifications", + Help: commands.HelpMeta{ + Section: HelpSectionConnectionManagement, + Description: "Silence Connected statuses when session changes and no data received recently.", + }, +} + +func fnToggleVerboseNotifications(ce *WrappedCommandEvent) { + ce.User.toggleNotifyVerbose() + if ce.User.DisableNotifyVerbose { + ce.Reply("Disabled verbose notifications") + } else { + ce.Reply("Enabled verbose notifications") + } + ce.ZLog.Trace().Msg("ToggleVerboseNotifications command finished") +} + var cmdPM = &commands.FullHandler{ Func: wrapCommand(fnPM), Name: "pm", diff --git a/database/upgrades/11-user-settings.sql b/database/upgrades/11-user-settings.sql new file mode 100644 index 0000000..b5a402c --- /dev/null +++ b/database/upgrades/11-user-settings.sql @@ -0,0 +1,3 @@ +-- v11 (compatible with v10+): User settings +ALTER TABLE "user" ADD COLUMN disable_notify_battery BOOLEAN NOT NULL DEFAULT false; +ALTER TABLE "user" ADD COLUMN disable_notify_verbose BOOLEAN NOT NULL DEFAULT false; diff --git a/database/user.go b/database/user.go index 6e0294e..a5315ad 100644 --- a/database/user.go +++ b/database/user.go @@ -40,7 +40,7 @@ func newUser(qh *dbutil.QueryHelper[*User]) *User { } const ( - getUserBaseQuery = `SELECT rowid, mxid, phone_id, session, self_participant_ids, sim_metadata, settings, management_room, space_room, access_token FROM "user"` + getUserBaseQuery = `SELECT rowid, mxid, phone_id, session, self_participant_ids, sim_metadata, settings, management_room, space_room, access_token, disable_notify_battery, disable_notify_verbose FROM "user"` getAllUsersWithSessionQuery = getUserBaseQuery + " WHERE session IS NOT NULL" getAllUsersWithDoublePuppetQuery = getUserBaseQuery + " WHERE access_token<>''" getUserByRowIDQuery = getUserBaseQuery + " WHERE rowid=$1" @@ -53,7 +53,8 @@ const ( updateUserQuery = ` 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 + management_room=$7, space_room=$8, access_token=$9, + disable_notify_battery=$10, disable_notify_verbose=$11 WHERE mxid=$1 ` updateuserParticipantIDsQuery = `UPDATE "user" SET self_participant_ids=$2 WHERE mxid=$1` @@ -103,6 +104,9 @@ type User struct { Settings Settings AccessToken string + + DisableNotifyBattery bool + DisableNotifyVerbose bool } func (user *User) Scan(row dbutil.Scannable) (*User, error) { @@ -111,6 +115,7 @@ func (user *User) Scan(row dbutil.Scannable) (*User, error) { err := row.Scan( &user.RowID, &user.MXID, &phoneID, &session, &selfParticipantIDs, &simMetadata, &settings, &managementRoom, &spaceRoom, &accessToken, + &user.DisableNotifyBattery, &user.DisableNotifyVerbose, ) if err != nil { return nil, err @@ -169,6 +174,7 @@ func (user *User) sqlVariables() []any { return []any{ user.MXID, dbutil.StrPtr(user.PhoneID), session, string(selfParticipantIDs), string(simMetadata), string(settings), dbutil.StrPtr(user.ManagementRoom), dbutil.StrPtr(user.SpaceRoom), dbutil.StrPtr(user.AccessToken), + user.DisableNotifyBattery, user.DisableNotifyVerbose, } } diff --git a/libgm/events/ready.go b/libgm/events/ready.go index ddddecc..6c8cdd8 100644 --- a/libgm/events/ready.go +++ b/libgm/events/ready.go @@ -19,6 +19,8 @@ type GaiaLoggedOut struct{} type NoDataReceived struct{} +type RecentlyDisconnected struct{} + type AccountChange struct { *gmproto.AccountChangeOrSomethingEvent IsFake bool diff --git a/libgm/longpoll.go b/libgm/longpoll.go index 503d0c7..52b3067 100644 --- a/libgm/longpoll.go +++ b/libgm/longpoll.go @@ -240,7 +240,7 @@ func (dp *dittoPinger) Loop() { go dp.HandleNoRecentUpdates() } else if time.Since(pingStart) > 5*time.Minute { dp.log.Warn().Msg("Was disconnected for over 5 minutes, sending extra GET_UPDATES call") - go dp.HandleNoRecentUpdates() + go dp.HandleRecentlyDisconnected() } } } @@ -259,6 +259,20 @@ func (dp *dittoPinger) HandleNoRecentUpdates() { } } +func (dp *dittoPinger) HandleRecentlyDisconnected() { + dp.client.triggerEvent(&events.RecentlyDisconnected{}) + err := dp.client.sessionHandler.sendMessageNoResponse(SendMessageParams{ + Action: gmproto.ActionType_GET_UPDATES, + OmitTTL: true, + RequestID: dp.client.sessionHandler.sessionID, + }) + if err != nil { + dp.log.Err(err).Msg("Failed to send extra GET_UPDATES call") + } else { + dp.log.Debug().Msg("Sent extra GET_UPDATES call") + } +} + func (c *Client) shouldDoDataReceiveCheck() bool { c.nextDataReceiveCheckLock.Lock() defer c.nextDataReceiveCheckLock.Unlock() diff --git a/user.go b/user.go index a56e6e9..2a82ff3 100644 --- a/user.go +++ b/user.go @@ -79,6 +79,7 @@ type User struct { phoneNotRespondingAlertSent bool didHackySetActive bool noDataReceivedRecently bool + recentlyDisconnected bool lastDataReceived time.Time gaiaHackyDeviceSwitcher int @@ -660,6 +661,7 @@ func (user *User) DeleteSession() { user.SelfParticipantIDs = []string{} user.didHackySetActive = false user.noDataReceivedRecently = false + user.recentlyDisconnected = false user.lastDataReceived = time.Time{} err := user.Update(context.TODO()) if err != nil { @@ -849,6 +851,8 @@ func (user *User) syncHandleEvent(event any) { user.handleSettings(v) case *events.AccountChange: user.handleAccountChange(v) + case *events.RecentlyDisconnected: + user.recentlyDisconnected = true case *events.NoDataReceived: user.noDataReceivedRecently = true default: @@ -970,17 +974,20 @@ func (user *User) handleUserAlert(v *gmproto.UserAlertEvent) { user.ready = true newSessionID := user.Client.CurrentSessionID() sessionIDChanged := user.sessionID != newSessionID - if sessionIDChanged || wasInactive || user.noDataReceivedRecently { + if sessionIDChanged || wasInactive || user.noDataReceivedRecently || user.recentlyDisconnected { user.zlog.Debug(). Str("old_session_id", user.sessionID). Str("new_session_id", newSessionID). Bool("was_inactive", wasInactive). Bool("had_no_data_received", user.noDataReceivedRecently). + Bool("recently_disconeccted", user.recentlyDisconnected). Time("last_data_received", user.lastDataReceived). Msg("Session ID changed for browser active event, resyncing") user.sessionID = newSessionID go user.fetchAndSyncConversations(user.lastDataReceived, !sessionIDChanged && !wasInactive) - go user.sendMarkdownBridgeAlert(ctx, false, "Connected to Google Messages") + if (!user.DisableNotifyVerbose || wasInactive || user.recentlyDisconnected) { + go user.sendMarkdownBridgeAlert(ctx, false, "Connected to Google Messages") + } } else { user.zlog.Debug(). Str("session_id", user.sessionID). @@ -990,6 +997,7 @@ func (user *User) handleUserAlert(v *gmproto.UserAlertEvent) { Msg("Session ID didn't change for browser active event, not resyncing") } user.noDataReceivedRecently = false + user.recentlyDisconnected = false user.lastDataReceived = time.Now() case gmproto.AlertType_BROWSER_INACTIVE_FROM_TIMEOUT: user.browserInactiveType = GMBrowserInactiveTimeout @@ -1004,13 +1012,17 @@ func (user *User) handleUserAlert(v *gmproto.UserAlertEvent) { case gmproto.AlertType_MOBILE_BATTERY_LOW: user.batteryLow = true if time.Since(user.batteryLowAlertSent) > 30*time.Minute { - go user.sendMarkdownBridgeAlert(ctx, true, "Your phone's battery is low") + if (!user.DisableNotifyBattery) { + go user.sendMarkdownBridgeAlert(ctx, true, "Your phone's battery is low") + } user.batteryLowAlertSent = time.Now() } case gmproto.AlertType_MOBILE_BATTERY_RESTORED: user.batteryLow = false if !user.batteryLowAlertSent.IsZero() { - go user.sendMarkdownBridgeAlert(ctx, false, "Phone battery restored") + if (!user.DisableNotifyBattery) { + go user.sendMarkdownBridgeAlert(ctx, false, "Phone battery restored") + } user.batteryLowAlertSent = time.Time{} } default: @@ -1026,6 +1038,22 @@ func (user *User) handleUserAlert(v *gmproto.UserAlertEvent) { user.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected}) } +func (user *User) toggleNotifyBattery() { + user.DisableNotifyBattery = !user.DisableNotifyBattery + err := user.Update(context.TODO()) + if err != nil { + user.zlog.Err(err).Msg("Failed to save notify battery preference") + } +} + +func (user *User) toggleNotifyVerbose() { + user.DisableNotifyVerbose = !user.DisableNotifyVerbose + err := user.Update(context.TODO()) + if err != nil { + user.zlog.Err(err).Msg("Failed to save notify verbose preference") + } +} + func (user *User) handleSettings(settings *gmproto.Settings) { if settings.SIMCards == nil { return