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