github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/attachments/sender.go (about)

     1  package attachments
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/keybase/client/go/chat/globals"
     7  	"github.com/keybase/client/go/chat/storage"
     8  	"github.com/keybase/client/go/chat/types"
     9  	"github.com/keybase/client/go/chat/utils"
    10  	"github.com/keybase/client/go/protocol/chat1"
    11  	"github.com/keybase/client/go/protocol/gregor1"
    12  	"github.com/keybase/client/go/protocol/keybase1"
    13  	"golang.org/x/net/context"
    14  )
    15  
    16  type Sender struct {
    17  	globals.Contextified
    18  	utils.DebugLabeler
    19  }
    20  
    21  func NewSender(g *globals.Context) *Sender {
    22  	return &Sender{
    23  		Contextified: globals.NewContextified(g),
    24  		DebugLabeler: utils.NewDebugLabeler(g.ExternalG(), "Attachments.Sender", false),
    25  	}
    26  }
    27  
    28  func (s *Sender) MakePreview(ctx context.Context, filename string, outboxID chat1.OutboxID) (res chat1.MakePreviewRes, err error) {
    29  	defer s.Trace(ctx, &err, "MakePreview")()
    30  	src, err := NewReadCloseResetter(ctx, s.G().GlobalContext, filename)
    31  	if err != nil {
    32  		return res, err
    33  	}
    34  	defer src.Close()
    35  	pre, err := PreprocessAsset(ctx, s.G(), s.DebugLabeler, src, filename, s.G().NativeVideoHelper, nil)
    36  	if err != nil {
    37  		return chat1.MakePreviewRes{}, err
    38  	}
    39  	if pre.Preview != nil {
    40  		if err := NewPendingPreviews(s.G()).Put(ctx, outboxID, pre); err != nil {
    41  			return res, err
    42  		}
    43  		return pre.Export(func() *chat1.PreviewLocation {
    44  			loc := chat1.NewPreviewLocationWithUrl(s.G().AttachmentURLSrv.GetPendingPreviewURL(ctx, outboxID))
    45  			return &loc
    46  		})
    47  	}
    48  	return pre.Export(func() *chat1.PreviewLocation { return nil })
    49  }
    50  
    51  func (s *Sender) preprocess(ctx context.Context, filename string, callerPreview *chat1.MakePreviewRes) (res Preprocess, err error) {
    52  	src, err := NewReadCloseResetter(ctx, s.G().GlobalContext, filename)
    53  	if err != nil {
    54  		return res, err
    55  	}
    56  	defer src.Close()
    57  	return PreprocessAsset(ctx, s.G(), s.DebugLabeler, src, filename, s.G().NativeVideoHelper, callerPreview)
    58  }
    59  
    60  func (s *Sender) makeBaseAttachmentMessage(ctx context.Context, tlfName string, vis keybase1.TLFVisibility,
    61  	inOutboxID *chat1.OutboxID, filename, title string, md []byte, ephemeralLifetime *gregor1.DurationSec,
    62  	callerPreview *chat1.MakePreviewRes) (msg chat1.MessagePlaintext, outboxID chat1.OutboxID, err error) {
    63  	if inOutboxID == nil {
    64  		if outboxID, err = storage.NewOutboxID(); err != nil {
    65  			return msg, outboxID, err
    66  		}
    67  	} else {
    68  		outboxID = *inOutboxID
    69  	}
    70  
    71  	var assetMetadata chat1.AssetMetadata
    72  	if pre, err := s.preprocess(ctx, filename, callerPreview); err != nil {
    73  		// If we can't generate a preview here, let's not blow the whole thing up, we can try
    74  		// again when we are actually uploading the attachment
    75  		s.Debug(ctx, "makeBaseAttachmentMessage: failed to process caller preview, skipping: %s", err)
    76  	} else {
    77  		if err := NewPendingPreviews(s.G()).Put(ctx, outboxID, pre); err != nil {
    78  			s.Debug(ctx, "makeBaseAttachmentMessage: failed to save pending preview: %s", err)
    79  		}
    80  		assetMetadata = pre.BaseMetadata()
    81  	}
    82  
    83  	msg = chat1.MessagePlaintext{
    84  		ClientHeader: chat1.MessageClientHeader{
    85  			MessageType: chat1.MessageType_ATTACHMENT,
    86  			TlfName:     tlfName,
    87  			TlfPublic:   vis == keybase1.TLFVisibility_PUBLIC,
    88  			OutboxID:    &outboxID,
    89  		},
    90  		MessageBody: chat1.NewMessageBodyWithAttachment(chat1.MessageAttachment{
    91  			Object: chat1.Asset{
    92  				Title:    title,
    93  				Filename: filename,
    94  				Metadata: assetMetadata,
    95  			},
    96  			Metadata: md,
    97  		}),
    98  	}
    99  	if ephemeralLifetime != nil {
   100  		msg.ClientHeader.EphemeralMetadata = &chat1.MsgEphemeralMetadata{
   101  			Lifetime: *ephemeralLifetime,
   102  		}
   103  	}
   104  
   105  	return msg, outboxID, nil
   106  }
   107  
   108  func (s *Sender) PostFileAttachmentMessage(ctx context.Context, sender types.Sender,
   109  	convID chat1.ConversationID, tlfName string, vis keybase1.TLFVisibility, inOutboxID *chat1.OutboxID,
   110  	filename, title string, md []byte, clientPrev chat1.MessageID, ephemeralLifetime *gregor1.DurationSec,
   111  	callerPreview *chat1.MakePreviewRes) (outboxID chat1.OutboxID, msgID *chat1.MessageID, err error) {
   112  	defer s.Trace(ctx, &err, "PostFileAttachmentMessage")()
   113  	var msg chat1.MessagePlaintext
   114  	if msg, outboxID, err = s.makeBaseAttachmentMessage(ctx, tlfName, vis, inOutboxID, filename, title, md,
   115  		ephemeralLifetime, callerPreview); err != nil {
   116  		return outboxID, msgID, err
   117  	}
   118  	s.Debug(ctx, "PostFileAttachmentMessage: generated message with outbox ID: %s", outboxID)
   119  	_, boxed, err := sender.Send(ctx, convID, msg, clientPrev, &outboxID, nil, nil)
   120  	if err != nil {
   121  		return outboxID, msgID, err
   122  	}
   123  	if boxed != nil && boxed.ServerHeader != nil {
   124  		msgID = new(chat1.MessageID)
   125  		*msgID = boxed.GetMessageID()
   126  	}
   127  	return outboxID, msgID, nil
   128  }
   129  
   130  func (s *Sender) PostFileAttachment(ctx context.Context, sender types.Sender, uid gregor1.UID,
   131  	convID chat1.ConversationID, tlfName string, vis keybase1.TLFVisibility, inOutboxID *chat1.OutboxID,
   132  	filename, title string, md []byte, clientPrev chat1.MessageID, ephemeralLifetime *gregor1.DurationSec,
   133  	callerPreview *chat1.MakePreviewRes) (outboxID chat1.OutboxID, msgID *chat1.MessageID, err error) {
   134  	defer s.Trace(ctx, &err, "PostFileAttachment")()
   135  	var msg chat1.MessagePlaintext
   136  	if msg, outboxID, err = s.makeBaseAttachmentMessage(ctx, tlfName, vis, inOutboxID, filename, title, md,
   137  		ephemeralLifetime, callerPreview); err != nil {
   138  		return outboxID, msgID, err
   139  	}
   140  	// Start upload
   141  	uresCb, err := s.G().AttachmentUploader.Register(ctx, uid, convID, outboxID, title, filename, md,
   142  		callerPreview)
   143  	if err != nil {
   144  		return outboxID, msgID, err
   145  	}
   146  	// Wait for upload
   147  	ures := <-uresCb.Wait()
   148  	if ures.Error != nil {
   149  		s.Debug(ctx, "PostFileAttachment: upload failed, bailing out: %s", *ures.Error)
   150  		return outboxID, msgID, errors.New(*ures.Error)
   151  	}
   152  	// Fill in the rest of the message
   153  	attachment := chat1.MessageAttachment{
   154  		Object:   ures.Object,
   155  		Metadata: md,
   156  		Uploaded: true,
   157  	}
   158  	if ures.Preview != nil {
   159  		s.Debug(ctx, "PostFileAttachment: attachment preview asset added")
   160  		attachment.Previews = []chat1.Asset{*ures.Preview}
   161  		attachment.Preview = ures.Preview
   162  	}
   163  	msg.MessageBody = chat1.NewMessageBodyWithAttachment(attachment)
   164  
   165  	s.Debug(ctx, "PostFileAttachment: attachment assets uploaded, posting attachment message")
   166  	_, boxed, err := sender.Send(ctx, convID, msg, clientPrev, &outboxID, nil, nil)
   167  	if err != nil {
   168  		return outboxID, msgID, err
   169  	}
   170  	if boxed != nil && boxed.ServerHeader != nil {
   171  		msgID = new(chat1.MessageID)
   172  		*msgID = boxed.GetMessageID()
   173  	}
   174  	return outboxID, msgID, nil
   175  }