github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/chat/mobilepush.go (about)

     1  package chat
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"time"
     7  
     8  	"github.com/keybase/client/go/chat/globals"
     9  	"github.com/keybase/client/go/libkb"
    10  
    11  	"github.com/keybase/client/go/chat/storage"
    12  	"github.com/keybase/client/go/chat/utils"
    13  	"github.com/keybase/client/go/protocol/chat1"
    14  	"github.com/keybase/client/go/protocol/gregor1"
    15  	"github.com/keybase/client/go/protocol/keybase1"
    16  	"github.com/keybase/go-codec/codec"
    17  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    18  )
    19  
    20  type remoteNotificationSuccessHandler struct{}
    21  
    22  func (g *remoteNotificationSuccessHandler) HandlerName() string {
    23  	return "remote notification success"
    24  }
    25  func (g *remoteNotificationSuccessHandler) OnConnect(ctx context.Context, conn *rpc.Connection, cli rpc.GenericClient, srv *rpc.Server) error {
    26  	return nil
    27  }
    28  func (g *remoteNotificationSuccessHandler) OnConnectError(err error, reconnectThrottleDuration time.Duration) {
    29  }
    30  func (g *remoteNotificationSuccessHandler) OnDisconnected(ctx context.Context, status rpc.DisconnectStatus) {
    31  }
    32  func (g *remoteNotificationSuccessHandler) OnDoCommandError(err error, nextTime time.Duration) {}
    33  func (g *remoteNotificationSuccessHandler) ShouldRetry(name string, err error) bool {
    34  	return false
    35  }
    36  func (g *remoteNotificationSuccessHandler) ShouldRetryOnConnect(err error) bool {
    37  	return false
    38  }
    39  
    40  type MobilePush struct {
    41  	globals.Contextified
    42  	utils.DebugLabeler
    43  }
    44  
    45  func NewMobilePush(g *globals.Context) *MobilePush {
    46  	return &MobilePush{
    47  		Contextified: globals.NewContextified(g),
    48  		DebugLabeler: utils.NewDebugLabeler(g.ExternalG(), "MobilePush", false),
    49  	}
    50  }
    51  
    52  func (h *MobilePush) AckNotificationSuccess(ctx context.Context, pushIDs []string) {
    53  	defer h.Trace(ctx, nil, "AckNotificationSuccess: pushID: %v", pushIDs)()
    54  	conn, token, err := utils.GetGregorConn(ctx, h.G(), h.DebugLabeler,
    55  		func(nist *libkb.NIST) rpc.ConnectionHandler {
    56  			return &remoteNotificationSuccessHandler{}
    57  		})
    58  	if err != nil {
    59  		return
    60  	}
    61  	defer conn.Shutdown()
    62  
    63  	// Make remote successful call on our ad hoc conn
    64  	cli := chat1.RemoteClient{Cli: NewRemoteClient(h.G(), conn.GetClient())}
    65  	if err = cli.RemoteNotificationSuccessful(ctx,
    66  		chat1.RemoteNotificationSuccessfulArg{
    67  			AuthToken:        token,
    68  			CompanionPushIDs: pushIDs,
    69  		}); err != nil {
    70  		h.Debug(ctx, "AckNotificationSuccess: failed to invoke remote notification success: %s", err)
    71  	}
    72  }
    73  
    74  func (h *MobilePush) UnboxPushNotification(ctx context.Context, uid gregor1.UID,
    75  	convID chat1.ConversationID, membersType chat1.ConversationMembersType, payload string) (res chat1.MessageUnboxed, err error) {
    76  	defer h.Trace(ctx, &err, "UnboxPushNotification: convID: %v", convID)()
    77  	// Parse the message payload
    78  	bMsg, err := base64.StdEncoding.DecodeString(payload)
    79  	if err != nil {
    80  		h.Debug(ctx, "UnboxPushNotification: invalid message payload: %s", err)
    81  		return res, err
    82  	}
    83  	var msgBoxed chat1.MessageBoxed
    84  	mh := codec.MsgpackHandle{WriteExt: true}
    85  	if err = codec.NewDecoderBytes(bMsg, &mh).Decode(&msgBoxed); err != nil {
    86  		h.Debug(ctx, "UnboxPushNotification: failed to msgpack decode payload: %s", err)
    87  		return res, err
    88  	}
    89  
    90  	// Unbox first
    91  	vis := keybase1.TLFVisibility_PRIVATE
    92  	if msgBoxed.ClientHeader.TlfPublic {
    93  		vis = keybase1.TLFVisibility_PUBLIC
    94  	}
    95  	unboxInfo := newBasicUnboxConversationInfo(convID, membersType, nil, vis)
    96  	msgUnboxed, err := NewBoxer(h.G()).UnboxMessage(ctx, msgBoxed, unboxInfo, nil)
    97  	if err != nil {
    98  		h.Debug(ctx, "UnboxPushNotification: unbox failed, bailing: %s", err)
    99  		return res, err
   100  	}
   101  
   102  	// Check to see if this will be a strict append before adding to the body cache
   103  	if err := h.G().ConvSource.AcquireConversationLock(ctx, uid, convID); err != nil {
   104  		return res, err
   105  	}
   106  	maxMsgID, err := storage.New(h.G(), h.G().ConvSource).GetMaxMsgID(ctx, convID, uid)
   107  	if err == nil {
   108  		if msgUnboxed.GetMessageID() > maxMsgID {
   109  			if err = h.G().ConvSource.PushUnboxed(ctx, unboxInfo, uid, []chat1.MessageUnboxed{msgUnboxed}); err != nil {
   110  				h.Debug(ctx, "UnboxPushNotification: failed to push message to conv source: %s",
   111  					err.Error())
   112  			}
   113  		} else {
   114  			h.Debug(ctx, "UnboxPushNotification: message from the past, skipping insert: msgID: %d maxMsgID: %d", msgUnboxed.GetMessageID(), maxMsgID)
   115  		}
   116  	} else {
   117  		h.Debug(ctx, "UnboxPushNotification: failed to fetch max msg ID: %s", err)
   118  	}
   119  	h.G().ConvSource.ReleaseConversationLock(ctx, uid, convID)
   120  	return msgUnboxed, nil
   121  }