forked from MirrorHub/mautrix-whatsapp
Try to track when the phone is online and warn user if it's offline for too long
This commit is contained in:
parent
aacb1d57df
commit
e8c77c7ec6
12 changed files with 154 additions and 42 deletions
|
@ -47,17 +47,19 @@ const (
|
|||
type BridgeErrorCode string
|
||||
|
||||
const (
|
||||
WANotLoggedIn BridgeErrorCode = "wa-logged-out"
|
||||
WALoggedOut BridgeErrorCode = "wa-logged-out"
|
||||
WANotConnected BridgeErrorCode = "wa-not-connected"
|
||||
WAConnecting BridgeErrorCode = "wa-connecting"
|
||||
WAServerTimeout BridgeErrorCode = "wa-server-timeout"
|
||||
WAPhoneOffline BridgeErrorCode = "wa-phone-offline"
|
||||
)
|
||||
|
||||
var bridgeHumanErrors = map[BridgeErrorCode]string{
|
||||
WANotLoggedIn: "You're not logged into WhatsApp",
|
||||
WALoggedOut: "You were logged out from another device. Relogin to continue using the bridge.",
|
||||
WANotConnected: "You're not connected to WhatsApp",
|
||||
WAConnecting: "Reconnecting to WhatsApp...",
|
||||
WAServerTimeout: "The WhatsApp web servers are not responding. The bridge will try to reconnect.",
|
||||
WAPhoneOffline: "Your phone hasn't been seen in over 12 days. The bridge is currently connected, but will get disconnected if you don't open the app soon.",
|
||||
}
|
||||
|
||||
type BridgeState struct {
|
||||
|
@ -172,6 +174,13 @@ func (user *User) sendBridgeState(state BridgeState) {
|
|||
}
|
||||
}
|
||||
|
||||
func (user *User) GetPrevBridgeState() BridgeState {
|
||||
if user.prevBridgeStatus != nil {
|
||||
return *user.prevBridgeStatus
|
||||
}
|
||||
return BridgeState{}
|
||||
}
|
||||
|
||||
func (prov *ProvisioningAPI) BridgeStatePing(w http.ResponseWriter, r *http.Request) {
|
||||
if !prov.bridge.AS.CheckServerToken(w, r) {
|
||||
return
|
||||
|
|
|
@ -75,6 +75,8 @@ type BridgeConfig struct {
|
|||
|
||||
DisappearingMessagesInGroups bool `yaml:"disappearing_messages_in_groups"`
|
||||
|
||||
DisableBridgeAlerts bool `yaml:"disable_bridge_alerts"`
|
||||
|
||||
CommandPrefix string `yaml:"command_prefix"`
|
||||
|
||||
ManagementRoomText struct {
|
||||
|
|
|
@ -103,6 +103,7 @@ func (helper *UpgradeHelper) doUpgrade() {
|
|||
helper.Copy(Str, "bridge", "command_prefix")
|
||||
helper.Copy(Bool, "bridge", "federate_rooms")
|
||||
helper.Copy(Bool, "bridge", "disappearing_messages_in_groups")
|
||||
helper.Copy(Bool, "bridge", "disable_bridge_alerts")
|
||||
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")
|
||||
|
|
10
database/upgrades/2022-01-24-phone-last-seen-ts.go
Normal file
10
database/upgrades/2022-01-24-phone-last-seen-ts.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package upgrades
|
||||
|
||||
import "database/sql"
|
||||
|
||||
func init() {
|
||||
upgrades[35] = upgrade{"Store approximate last seen timestamp of the main device", func(tx *sql.Tx, ctx context) error {
|
||||
_, err := tx.Exec(`ALTER TABLE "user" ADD COLUMN phone_last_seen BIGINT`)
|
||||
return err
|
||||
}}
|
||||
}
|
|
@ -40,7 +40,7 @@ type upgrade struct {
|
|||
fn upgradeFunc
|
||||
}
|
||||
|
||||
const NumberOfUpgrades = 35
|
||||
const NumberOfUpgrades = 36
|
||||
|
||||
var upgrades [NumberOfUpgrades]upgrade
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ func (uq *UserQuery) New() *User {
|
|||
}
|
||||
|
||||
func (uq *UserQuery) GetAll() (users []*User) {
|
||||
rows, err := uq.db.Query(`SELECT mxid, username, agent, device, management_room, space_room FROM "user"`)
|
||||
rows, err := uq.db.Query(`SELECT mxid, username, agent, device, management_room, space_room, phone_last_seen FROM "user"`)
|
||||
if err != nil || rows == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ 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, space_room FROM "user" WHERE mxid=$1`, userID)
|
||||
row := uq.db.QueryRow(`SELECT mxid, username, agent, device, management_room, space_room, phone_last_seen FROM "user" WHERE mxid=$1`, userID)
|
||||
if row == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ func (uq *UserQuery) GetByMXID(userID id.UserID) *User {
|
|||
}
|
||||
|
||||
func (uq *UserQuery) GetByUsername(username string) *User {
|
||||
row := uq.db.QueryRow(`SELECT mxid, username, agent, device, management_room, space_room FROM "user" WHERE username=$1`, username)
|
||||
row := uq.db.QueryRow(`SELECT mxid, username, agent, device, management_room, space_room, phone_last_seen FROM "user" WHERE username=$1`, username)
|
||||
if row == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ type User struct {
|
|||
JID types.JID
|
||||
ManagementRoom id.RoomID
|
||||
SpaceRoom id.RoomID
|
||||
PhoneLastSeen time.Time
|
||||
|
||||
lastReadCache map[PortalKey]time.Time
|
||||
lastReadCacheLock sync.Mutex
|
||||
|
@ -89,7 +90,8 @@ type User struct {
|
|||
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, &user.SpaceRoom)
|
||||
var phoneLastSeen sql.NullInt64
|
||||
err := row.Scan(&user.MXID, &username, &agent, &device, &user.ManagementRoom, &user.SpaceRoom, &phoneLastSeen)
|
||||
if err != nil {
|
||||
if err != sql.ErrNoRows {
|
||||
user.log.Errorln("Database scan failed:", err)
|
||||
|
@ -99,6 +101,9 @@ func (user *User) Scan(row Scannable) *User {
|
|||
if len(username.String) > 0 {
|
||||
user.JID = types.NewADJID(username.String, agent.Byte, device.Byte)
|
||||
}
|
||||
if phoneLastSeen.Valid {
|
||||
user.PhoneLastSeen = time.Unix(phoneLastSeen.Int64, 0)
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
|
@ -123,17 +128,25 @@ func (user *User) devicePtr() *uint8 {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (user *User) phoneLastSeenPtr() *int64 {
|
||||
if user.PhoneLastSeen.IsZero() {
|
||||
return nil
|
||||
}
|
||||
ts := user.PhoneLastSeen.Unix()
|
||||
return &ts
|
||||
}
|
||||
|
||||
func (user *User) Insert() {
|
||||
_, 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)
|
||||
_, err := user.db.Exec(`INSERT INTO "user" (mxid, username, agent, device, management_room, space_room, phone_last_seen) VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
||||
user.MXID, user.usernamePtr(), user.agentPtr(), user.devicePtr(), user.ManagementRoom, user.SpaceRoom, user.phoneLastSeenPtr())
|
||||
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, space_room=$5 WHERE mxid=$6`,
|
||||
user.usernamePtr(), user.agentPtr(), user.devicePtr(), user.ManagementRoom, user.SpaceRoom, user.MXID)
|
||||
_, err := user.db.Exec(`UPDATE "user" SET username=$1, agent=$2, device=$3, management_room=$4, space_room=$5, phone_last_seen=$6 WHERE mxid=$7`,
|
||||
user.usernamePtr(), user.agentPtr(), user.devicePtr(), user.ManagementRoom, user.SpaceRoom, user.phoneLastSeenPtr(), user.MXID)
|
||||
if err != nil {
|
||||
user.log.Warnfln("Failed to update %s: %v", user.MXID, err)
|
||||
}
|
||||
|
|
11
disappear.go
11
disappear.go
|
@ -50,13 +50,10 @@ func (portal *Portal) ScheduleDisappearing() {
|
|||
}
|
||||
}
|
||||
|
||||
func (bridge *Bridge) DisappearingLoop() {
|
||||
for {
|
||||
for _, msg := range bridge.DB.DisappearingMessage.GetUpcomingScheduled(1 * time.Hour) {
|
||||
portal := bridge.GetPortalByMXID(msg.RoomID)
|
||||
go portal.sleepAndDelete(msg)
|
||||
}
|
||||
time.Sleep(1 * time.Hour)
|
||||
func (bridge *Bridge) SleepAndDeleteUpcoming() {
|
||||
for _, msg := range bridge.DB.DisappearingMessage.GetUpcomingScheduled(1 * time.Hour) {
|
||||
portal := bridge.GetPortalByMXID(msg.RoomID)
|
||||
go portal.sleepAndDelete(msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -191,6 +191,9 @@ bridge:
|
|||
# the messages will be determined by the first user to read the message, rather than individually.
|
||||
# If the bridge only has a single user, this can be turned on safely.
|
||||
disappearing_messages_in_groups: false
|
||||
# Should the bridge never send alerts to the bridge management room?
|
||||
# These are mostly things like the user being logged out.
|
||||
disable_bridge_alerts: false
|
||||
|
||||
# The prefix for commands. Only required in non-management rooms.
|
||||
command_prefix: "!wa"
|
||||
|
|
8
go.mod
8
go.mod
|
@ -9,13 +9,13 @@ require (
|
|||
github.com/mattn/go-sqlite3 v1.14.10
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
go.mau.fi/whatsmeow v0.0.0-20220111203410-b078a9e90863
|
||||
go.mau.fi/whatsmeow v0.0.0-20220124150706-afc33ee3c21a
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
maunium.net/go/mauflag v1.0.0
|
||||
maunium.net/go/maulogger/v2 v2.3.2
|
||||
maunium.net/go/mautrix v0.10.11-0.20220117200125-6d9d537973fa
|
||||
maunium.net/go/mautrix v0.10.11-0.20220118151622-7cc9a5066c70
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -29,10 +29,10 @@ require (
|
|||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/tidwall/gjson v1.10.2 // indirect
|
||||
github.com/tidwall/gjson v1.13.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/sjson v1.2.3 // indirect
|
||||
github.com/tidwall/sjson v1.2.4 // indirect
|
||||
go.mau.fi/libsignal v0.0.0-20211109153248-a67163214910 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect
|
||||
|
|
17
go.sum
17
go.sum
|
@ -129,18 +129,19 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo=
|
||||
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M=
|
||||
github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
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/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.3 h1:5+deguEhHSEjmuICXZ21uSSsXotWMA0orU783+Z7Cp8=
|
||||
github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
|
||||
github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc=
|
||||
github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM=
|
||||
go.mau.fi/libsignal v0.0.0-20211109153248-a67163214910 h1:9FFhG0OmkuMau5UEaTgiUQ+7cSbtbOQ7hiWKdN8OI3I=
|
||||
go.mau.fi/libsignal v0.0.0-20211109153248-a67163214910/go.mod h1:AufGrvVh+00Nc07Jm4hTquh7yleZyn20tKJI2wCPAKg=
|
||||
go.mau.fi/whatsmeow v0.0.0-20220111203410-b078a9e90863 h1:5xGt9ghwG3XvlCAnq1WJuJ4mdOR6u/Ho5oYR0Ql9uFw=
|
||||
go.mau.fi/whatsmeow v0.0.0-20220111203410-b078a9e90863/go.mod h1:8jUjOAi3xtGubxcZgG8uSHpAdyQXBRbWAfxkctX/4y4=
|
||||
go.mau.fi/whatsmeow v0.0.0-20220124150706-afc33ee3c21a h1:e8aExGixi/O+kveh6S3wgydk9ogU5+gx0NqOmqWMapM=
|
||||
go.mau.fi/whatsmeow v0.0.0-20220124150706-afc33ee3c21a/go.mod h1:8jUjOAi3xtGubxcZgG8uSHpAdyQXBRbWAfxkctX/4y4=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
|
@ -222,5 +223,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/maulogger/v2 v2.3.2 h1:1XmIYmMd3PoQfp9J+PaHhpt80zpfmMqaShzUTC7FwY0=
|
||||
maunium.net/go/maulogger/v2 v2.3.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
|
||||
maunium.net/go/mautrix v0.10.11-0.20220117200125-6d9d537973fa h1:PA9cvjbiV2tqd5qgrnk42G4HfSLVWWqs27dKKvG1tCQ=
|
||||
maunium.net/go/mautrix v0.10.11-0.20220117200125-6d9d537973fa/go.mod h1:4XljZZGZiIlpfbQ+Tt2ykjapskJ8a7Z2i9y/+YaceF8=
|
||||
maunium.net/go/mautrix v0.10.11-0.20220118151622-7cc9a5066c70 h1:T/NnQ9DC/nSX+4wnpyl8tf+c8XjRxt7A3RAYfdAtevA=
|
||||
maunium.net/go/mautrix v0.10.11-0.20220118151622-7cc9a5066c70/go.mod h1:lm06wYU/IcPcdicMNrG9wj0t3xhqYpEA1k+4G9EGZwc=
|
||||
|
|
20
main.go
20
main.go
|
@ -333,10 +333,28 @@ func (bridge *Bridge) Start() {
|
|||
if bridge.Config.Bridge.ResendBridgeInfo {
|
||||
go bridge.ResendBridgeInfo()
|
||||
}
|
||||
go bridge.DisappearingLoop()
|
||||
go bridge.Loop()
|
||||
bridge.AS.Ready = true
|
||||
}
|
||||
|
||||
func (bridge *Bridge) Loop() {
|
||||
for {
|
||||
bridge.SleepAndDeleteUpcoming()
|
||||
time.Sleep(1 * time.Hour)
|
||||
bridge.WarnUsersAboutDisconnection()
|
||||
}
|
||||
}
|
||||
|
||||
func (bridge *Bridge) WarnUsersAboutDisconnection() {
|
||||
bridge.usersLock.Lock()
|
||||
for _, user := range bridge.usersByUsername {
|
||||
if user.IsConnected() && !user.PhoneRecentlySeen() {
|
||||
go user.sendPhoneOfflineWarning()
|
||||
}
|
||||
}
|
||||
bridge.usersLock.Unlock()
|
||||
}
|
||||
|
||||
func (bridge *Bridge) ResendBridgeInfo() {
|
||||
if *dontSaveConfig {
|
||||
bridge.Log.Warnln("Not setting resend_bridge_info to false in config due to --no-update flag")
|
||||
|
|
80
user.go
80
user.go
|
@ -21,6 +21,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -66,7 +67,8 @@ type User struct {
|
|||
prevBridgeStatus *BridgeState
|
||||
lastPresence types.Presence
|
||||
|
||||
spaceMembershipChecked bool
|
||||
spaceMembershipChecked bool
|
||||
lastPhoneOfflineWarning time.Time
|
||||
}
|
||||
|
||||
func (bridge *Bridge) getUserByMXID(userID id.UserID, onlyIfExists bool) *User {
|
||||
|
@ -119,7 +121,7 @@ func (user *User) removeFromJIDMap(state BridgeStateEvent) {
|
|||
}
|
||||
user.bridge.usersLock.Unlock()
|
||||
user.bridge.Metrics.TrackLoginState(user.JID, false)
|
||||
user.sendBridgeState(BridgeState{StateEvent: state, Error: WANotLoggedIn})
|
||||
user.sendBridgeState(BridgeState{StateEvent: state})
|
||||
}
|
||||
|
||||
func (bridge *Bridge) GetAllUsers() []*User {
|
||||
|
@ -425,15 +427,10 @@ func (user *User) tryAutomaticDoublePuppeting() {
|
|||
user.log.Infoln("Successfully automatically enabled custom puppet")
|
||||
}
|
||||
|
||||
func (user *User) sendBridgeNotice(formatString string, args ...interface{}) {
|
||||
notice := fmt.Sprintf(formatString, args...)
|
||||
_, err := user.bridge.Bot.SendNotice(user.GetManagementRoom(), notice)
|
||||
if err != nil {
|
||||
user.log.Warnf("Failed to send bridge notice \"%s\": %v", notice, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) sendMarkdownBridgeAlert(formatString string, args ...interface{}) {
|
||||
if user.bridge.Config.Bridge.DisableBridgeAlerts {
|
||||
return
|
||||
}
|
||||
notice := fmt.Sprintf(formatString, args...)
|
||||
content := format.RenderMarkdown(notice, true, false)
|
||||
_, err := user.bridge.Bot.SendMessageEvent(user.GetManagementRoom(), event.EventMessage, content)
|
||||
|
@ -465,6 +462,49 @@ func (user *User) handleCallStart(sender types.JID, id, callType string, ts time
|
|||
}
|
||||
}
|
||||
|
||||
const PhoneDisconnectWarningTime = 12 * 24 * time.Hour // 12 days
|
||||
|
||||
func (user *User) PhoneRecentlySeen() bool {
|
||||
return user.PhoneLastSeen.IsZero() || user.PhoneLastSeen.Add(PhoneDisconnectWarningTime).After(time.Now())
|
||||
}
|
||||
|
||||
// phoneSeen records a timestamp when the user's main device was seen online.
|
||||
// The stored timestamp can later be used to warn the user if the main device is offline for too long.
|
||||
func (user *User) phoneSeen(ts time.Time) {
|
||||
if user.PhoneLastSeen.Add(1 * time.Hour).After(ts) {
|
||||
// The last seen timestamp isn't going to be perfectly accurate in any case,
|
||||
// so don't spam the database with an update every time there's an event.
|
||||
return
|
||||
} else if !user.PhoneRecentlySeen() && user.GetPrevBridgeState().Error == WAPhoneOffline && user.IsConnected() {
|
||||
user.log.Debugfln("Saw phone after current bridge state said it has been offline, switching state back to connected")
|
||||
go user.sendBridgeState(BridgeState{StateEvent: StateConnected})
|
||||
}
|
||||
user.PhoneLastSeen = ts
|
||||
go user.Update()
|
||||
}
|
||||
|
||||
func formatDisconnectTime(dur time.Duration) string {
|
||||
days := int(math.Floor(dur.Hours() / 24))
|
||||
hours := int(dur.Hours()) % 24
|
||||
if hours == 0 {
|
||||
return fmt.Sprintf("%d days", days)
|
||||
} else if hours == 1 {
|
||||
return fmt.Sprintf("%d days and 1 hour", days)
|
||||
} else {
|
||||
return fmt.Sprintf("%d days and %d hours", days, hours)
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) sendPhoneOfflineWarning() {
|
||||
if user.lastPhoneOfflineWarning.Add(12 * time.Hour).After(time.Now()) {
|
||||
// Don't spam the warning too much
|
||||
return
|
||||
}
|
||||
user.lastPhoneOfflineWarning = time.Now()
|
||||
timeSinceSeen := time.Now().Sub(user.PhoneLastSeen)
|
||||
user.sendMarkdownBridgeAlert("Your phone hasn't been seen in %s. The server will force the bridge to log out if the phone is not active at least every 2 weeks.", formatDisconnectTime(timeSinceSeen))
|
||||
}
|
||||
|
||||
func (user *User) HandleEvent(event interface{}) {
|
||||
switch v := event.(type) {
|
||||
case *events.LoggedOut:
|
||||
|
@ -484,6 +524,20 @@ func (user *User) HandleEvent(event interface{}) {
|
|||
}()
|
||||
}
|
||||
go user.tryAutomaticDoublePuppeting()
|
||||
case *events.OfflineSyncPreview:
|
||||
user.log.Infofln("Server says it's going to send %d messages and %d receipts that were missed during downtime", v.Messages, v.Receipts)
|
||||
go user.sendBridgeState(BridgeState{
|
||||
StateEvent: StateBackfilling,
|
||||
Message: fmt.Sprintf("backfilling %d messages and %d receipts", v.Messages, v.Receipts),
|
||||
})
|
||||
case *events.OfflineSyncCompleted:
|
||||
if !user.PhoneRecentlySeen() {
|
||||
user.log.Infofln("Offline sync completed, but phone last seen date is still %s - sending phone offline bridge status", user.PhoneLastSeen)
|
||||
go user.sendBridgeState(BridgeState{StateEvent: StateTransientDisconnect, Error: WAPhoneOffline})
|
||||
} else if user.GetPrevBridgeState().StateEvent == StateBackfilling {
|
||||
user.log.Infoln("Offline sync completed")
|
||||
go user.sendBridgeState(BridgeState{StateEvent: StateConnected})
|
||||
}
|
||||
case *events.AppStateSyncComplete:
|
||||
if len(user.Client.Store.PushName) > 0 && v.Name == appstate.WAPatchCriticalBlock {
|
||||
err := user.Client.SendPresence(user.lastPresence)
|
||||
|
@ -506,6 +560,7 @@ func (user *User) HandleEvent(event interface{}) {
|
|||
user.log.Warnln("Failed to send presence after push name update:", err)
|
||||
}
|
||||
case *events.PairSuccess:
|
||||
user.PhoneLastSeen = time.Now()
|
||||
user.Session = user.Client.Store
|
||||
user.JID = v.ID
|
||||
user.addToJIDMap()
|
||||
|
@ -527,6 +582,9 @@ func (user *User) HandleEvent(event interface{}) {
|
|||
case *events.Picture:
|
||||
go user.handlePictureUpdate(v)
|
||||
case *events.Receipt:
|
||||
if v.IsFromMe && v.Sender.Device == 0 {
|
||||
user.phoneSeen(v.Timestamp)
|
||||
}
|
||||
go user.handleReceipt(v)
|
||||
case *events.ChatPresence:
|
||||
go user.handleChatPresence(v)
|
||||
|
@ -752,7 +810,7 @@ func (user *User) UpdateDirectChats(chats map[id.UserID][]id.RoomID) {
|
|||
}
|
||||
|
||||
func (user *User) handleLoggedOut(onConnect bool) {
|
||||
user.sendBridgeState(BridgeState{StateEvent: StateBadCredentials, Error: WANotLoggedIn})
|
||||
user.sendBridgeState(BridgeState{StateEvent: StateBadCredentials, Error: WALoggedOut})
|
||||
user.JID = types.EmptyJID
|
||||
user.Update()
|
||||
if onConnect {
|
||||
|
|
Loading…
Reference in a new issue