# Messaggi modificati ed eliminati

Quando un contatto **modifica** un messaggio (per esempio, per correggere un importo), ti arriva **`message.edited`**; quando lo **elimina**, **`message.revoked`**. Entrambi portano `originalMessageId`: il `wamid` del messaggio originale, lo stesso `data.message.id` che ti è arrivato nel suo `message.received`. Con quello aggiorni la riga che hai già salvato.

**Cosa arriva dipende dal tipo di numero** (lo decide Meta):

**Numero in coesistenza (app + API).** Modifica → `message.edited` con `textAvailable: true` e `data.message` = la **versione finale** (testo, o foto/video con la nuova didascalia), con la stessa forma di `message.received`. Eliminazione → `message.revoked`.

**Numero solo Cloud API.** Modifica → `message.edited` con `textAvailable: false`, `message: null` e `reason: "META_EDIT_TEXT_NOT_PROVIDED"`: Meta segnala la modifica ma **non invia il nuovo testo**. Eliminazione → Meta non la segnala.

**WhatsApp permette di modificare solo entro 15 minuti** dall'invio (e di eliminare, fino a 2 giorni). Una modifica senza testo è un limite di Meta: non c'è modo di chiedere il testo dopo.

**Il testo precedente non lo conserviamo** (WAIA Connect non salva il contenuto dei messaggi): se ti serve, è nel tuo database, sotto `originalMessageId`.

## Esempi

### cURL

```bash
# Nothing to call: WAIA Connect POSTs message.edited / message.revoked to your webhook.
# Read a saved body: the final text (or null when Meta did not send it) and the original id.
jq -r 'if .type == "message.edited"
  then "\(.data.originalMessageId) edited -> " + (if .data.textAvailable then (.data.message.text.body // .data.message.image.caption // .data.message.video.caption // "") else "(text not provided by Meta)" end)
  else "\(.data.originalMessageId) deleted" end' body.json
```

### Node.js

```javascript
// Inside your webhook handler, after verifying the signature.
const evt = req.body;
if (evt.type === "message.edited") {
  const d = evt.data;
  if (d.textAvailable) {
    const m = d.message; // same shape as message.received → data.message
    const text = m.text?.body ?? m.image?.caption ?? m.video?.caption ?? null;
    await db.messages.update({ wamid: d.originalMessageId }, { text, editedAt: d.editedAt }); // your code
  } else {
    // Cloud API-only number: Meta says it was edited but sends no text (reason META_EDIT_TEXT_NOT_PROVIDED)
    if (d.originalMessageId) await db.messages.update({ wamid: d.originalMessageId }, { editedAt: d.editedAt, textOutdated: true });
  }
} else if (evt.type === "message.revoked") {
  await db.messages.update({ wamid: evt.data.originalMessageId }, { deletedAt: evt.data.revokedAt }); // your code
}
```

### PHP

```php
<?php
// Inside your webhook handler, after verifying the signature.
$evt = json_decode(file_get_contents("php://input"), true);
$d = $evt["data"] ?? [];
if ($evt["type"] === "message.edited") {
  if ($d["textAvailable"]) {
    $m = $d["message"]; // same shape as message.received → data.message
    $text = $m["text"]["body"] ?? $m["image"]["caption"] ?? $m["video"]["caption"] ?? null;
    update_message($d["originalMessageId"], ["text" => $text, "edited_at" => $d["editedAt"]]); // your code
  } elseif ($d["originalMessageId"]) {
    // Cloud API-only number: Meta sends no text (reason META_EDIT_TEXT_NOT_PROVIDED)
    update_message($d["originalMessageId"], ["edited_at" => $d["editedAt"], "text_outdated" => true]);
  }
} elseif ($evt["type"] === "message.revoked") {
  update_message($d["originalMessageId"], ["deleted_at" => $d["revokedAt"]]); // your code
}
```

### Python

```python
# Inside your webhook handler, after verifying the signature.
evt = request.get_json()
d = evt.get("data", {})
if evt["type"] == "message.edited":
    if d["textAvailable"]:
        m = d["message"]  # same shape as message.received -> data.message
        text = (m.get("text") or {}).get("body") or (m.get("image") or {}).get("caption") or (m.get("video") or {}).get("caption")
        update_message(d["originalMessageId"], text=text, edited_at=d["editedAt"])  # your code
    elif d["originalMessageId"]:
        # Cloud API-only number: Meta sends no text (reason META_EDIT_TEXT_NOT_PROVIDED)
        update_message(d["originalMessageId"], edited_at=d["editedAt"], text_outdated=True)
elif evt["type"] == "message.revoked":
    update_message(d["originalMessageId"], deleted_at=d["revokedAt"])  # your code
```

## Note

- **Un webhook creato prima di questi eventi con «tutti gli eventi» non li riceve da solo**: scegli di nuovo «tutti» (`PATCH` con `events: null`, o il pulsante della sua riga nel pannello) o aggiungili all'elenco. `newEventsAvailable` dice quali mancano.
- Un endpoint che riceve `message.edited`/`message.revoked` non riceve più quella modifica o eliminazione grezza come `message.received` (`type: "edit"`, `"revoke"` o `"unsupported"`). Uno che non li riceve resta come prima.
- `originalConnectMessageId` è il nostro `msg_…` quando hai inviato l'originale tramite l'API; per un messaggio del contatto è `null`.
- Deduplica come qualsiasi evento: per l'`id` della busta (`evt_…`).
