MCPcopy Create free account
hub / github.com/Mnexa-AI/e2a / handleReply

Method handleReply

internal/httpapi/outbound.go:240–293  ·  view source on GitHub ↗
(ctx context.Context, in *replyInput)

Source from the content-addressed store, hash-verified

238}
239
240func (s *Server) handleReply(ctx context.Context, in *replyInput) (*sendOutput, error) {
241 ag, inbound, user, err := s.loadInbound(ctx, in.Address, in.ID)
242 if err != nil {
243 return nil, err
244 }
245 b := in.Body
246 if b.Body == "" {
247 return nil, NewError(http.StatusBadRequest, "invalid_request", "body is required")
248 }
249 // Validate only the user-supplied CC/BCC; the implicit To comes from the
250 // (already-validated) inbound message — mirrors the legacy handler.
251 if env := recipientCountError(b.CC, b.BCC); env != nil {
252 return nil, env
253 }
254 if e := agent.ValidateRecipients(b.CC, b.BCC); e != nil {
255 return nil, NewError(http.StatusBadRequest, "invalid_recipient", e.Error())
256 }
257 if e := validateConversationID(b.ConversationID); e != nil {
258 return nil, NewError(http.StatusBadRequest, "invalid_request", e.Error())
259 }
260 // Build the reply request via the same outbound helpers the legacy
261 // handler uses (subject normalization, recipient parsing, References).
262 subject := inbound.Subject
263 if subject != "" && !strings.HasPrefix(strings.ToLower(subject), "re: ") {
264 subject = "Re: " + subject
265 } else if subject == "" {
266 subject = "Re: your message"
267 }
268 rr, e := outbound.ParseReplyRecipients(inbound.RawMessage, b.ReplyAll, b.CC)
269 if e != nil {
270 return nil, NewError(http.StatusBadRequest, "invalid_recipient", e.Error())
271 }
272 replyTo := rr.To
273 if len(replyTo) == 0 {
274 replyTo = []string{inbound.Sender}
275 }
276 req := outbound.SendRequest{
277 To: replyTo, CC: rr.CC, BCC: b.BCC, Subject: subject, Body: b.Body, HTMLBody: b.HTMLBody,
278 ReplyToMessageID: inbound.EmailMessageID,
279 References: outbound.BuildReferencesChain(inbound.RawMessage, inbound.EmailMessageID),
280 ConversationID: b.ConversationID, Attachments: b.Attachments,
281 }
282 req.CC = agent.StripAgentSelfAliases(req.CC, ag.EmailAddress())
283 req.BCC = agent.StripAgentSelfAliases(req.BCC, ag.EmailAddress())
284 // Re-count the FINAL, post-expansion recipient set. reply_all fans the
285 // thread's To+Cc into req.To/req.CC above, so the earlier b.CC/b.BCC check is
286 // not the real fan-out — without this, a reply_all to a large thread bypasses
287 // the cap that /send and /forward enforce (the downstream send path has no
288 // cap of its own).
289 if env := recipientCountError(req.To, req.CC, req.BCC); env != nil {
290 return nil, env
291 }
292 return s.deliver(ctx, user, ag, req, "reply", inbound.EmailMessageID, "/v1/reply/"+in.ID, in.IdempotencyKey, in.RawBody, inbound)
293}
294
295// ForwardRequest mirrors the legacy forward body.
296type ForwardRequest struct {

Callers

nothing calls this directly

Calls 11

loadInboundMethod · 0.95
deliverMethod · 0.95
ValidateRecipientsFunction · 0.92
ParseReplyRecipientsFunction · 0.92
BuildReferencesChainFunction · 0.92
StripAgentSelfAliasesFunction · 0.92
NewErrorFunction · 0.85
recipientCountErrorFunction · 0.85
EmailAddressMethod · 0.80
validateConversationIDFunction · 0.70
ErrorMethod · 0.45

Tested by

no test coverage detected