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 }