CreateOutboundMessage stores an outbound message with multi-recipient support. The recipient param is kept for backward compat with the singular recipient column; toRecipients, cc, and bcc are the canonical outbound-only multi-recipient fields.
(ctx context.Context, agentID string, toRecipients []string, cc []string, bcc []string, subject, msgType, method, providerMessageID, conversationID string, rawMessage []byte)
| 1345 | // The recipient param is kept for backward compat with the singular recipient column; |
| 1346 | // toRecipients, cc, and bcc are the canonical outbound-only multi-recipient fields. |
| 1347 | func (s *Store) CreateOutboundMessage(ctx context.Context, agentID string, toRecipients []string, cc []string, bcc []string, subject, msgType, method, providerMessageID, conversationID string, rawMessage []byte) (*Message, error) { |
| 1348 | id := "msg_" + generateID() |
| 1349 | now := time.Now() |
| 1350 | |
| 1351 | // Use first To recipient as the singular recipient column for backward compat |
| 1352 | var recipient string |
| 1353 | if len(toRecipients) > 0 { |
| 1354 | recipient = toRecipients[0] |
| 1355 | } |
| 1356 | |
| 1357 | m := &Message{ |
| 1358 | ID: id, |
| 1359 | AgentID: agentID, |
| 1360 | Direction: "outbound", |
| 1361 | Recipient: recipient, |
| 1362 | Subject: subject, |
| 1363 | Type: msgType, |
| 1364 | Method: method, |
| 1365 | ProviderMessageID: providerMessageID, |
| 1366 | ConversationID: conversationID, |
| 1367 | CreatedAt: now, |
| 1368 | ExpiresAt: now.Add(MessageTTL), |
| 1369 | ToRecipients: toRecipients, |
| 1370 | CC: cc, |
| 1371 | BCC: bcc, |
| 1372 | // rawMessage is the composed MIME we actually sent — retained so the agent |
| 1373 | // has a readable Sent folder (nil for self-sends, whose body lives on the |
| 1374 | // inbound twin row; empty/NULL is fine). |
| 1375 | RawMessage: rawMessage, |
| 1376 | // The sender of an outbound message is the agent itself (agent ID == email). |
| 1377 | // Persist it so the `from` wire field isn't empty for outbound (B1). |
| 1378 | Sender: agentID, |
| 1379 | } |
| 1380 | _, err := s.pool.Exec(ctx, |
| 1381 | `INSERT INTO messages (id, agent_id, direction, recipient, subject, message_type, method, provider_message_id, conversation_id, created_at, expires_at, to_recipients, cc, bcc, status, sender, raw_message) |
| 1382 | VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)`, |
| 1383 | m.ID, m.AgentID, m.Direction, m.Recipient, m.Subject, m.Type, m.Method, m.ProviderMessageID, m.ConversationID, m.CreatedAt, m.ExpiresAt, m.ToRecipients, m.CC, m.BCC, MessageStatusSent, m.Sender, nullIfEmptyBytes(m.RawMessage), |
| 1384 | ) |
| 1385 | if err != nil { |
| 1386 | return nil, err |
| 1387 | } |
| 1388 | m.Status = MessageStatusSent |
| 1389 | return m, nil |
| 1390 | } |
| 1391 | |
| 1392 | // CreatePendingOutboundMessage stores a fully composed outbound email in |
| 1393 | // pending_review status, including body_text, body_html, and attachments so |