# Bot "crea e invia"

Da un link a un post pubblicato su un canale Telegram.

## I file

| file | cosa fa |
|---|---|
| `createandsend.php` | il webhook: conversazione, pulsanti, stato |
| `post_telegram.php` | dialogo con Telegram, immagini, costruzione del post |
| `setup_menu.php` | registra il menu dei comandi. **Da lanciare a mano una volta** |
| `create_webhook.php` | registra il webhook su Telegram (invariato) |
| `uploads/` | le immagini dei post personalizzati, cancellate dopo 7 giorni |
| `state_<chat_id>.json` | lo stato della conversazione di ogni utente |

## L'idea di fondo: un solo array di parametri

Tutto ruota intorno a `$stato['p']`, che descrive il post e non il modo in cui ci si è
arrivati:

```
url urlReale sito asin metodo smid usato
coupon coupon_price coupon_info custom_price
nota coupon_pagina_perc coupon_pagina_euro no_coupon
extra_sconto extra_sconto_price
sconti_db ae_sconti ae_off_coupon ae_off_pay
```

Ogni pulsante cambia **un campo** e richiama `costruisciPost()`. Da qui vengono, senza
codice dedicato:

- **Rigenera** e **Modifica post**: il link non si reincolla mai, perché non si perde mai.
- **Cambia venditore / metodo / coupon**: si sovrascrive un campo e si rifà il post.
- **Errore**: i parametri restano, e la schermata d'errore offre le vie d'uscita.

Prima ogni variante era un ramo `elseif` che rifaceva tutto da capo — cinque copie quasi
identiche dello stesso blocco — e l'unica uscita da un errore era ricominciare dal link.

## Cosa è cambiato il 04/08/2026

### La chat: un messaggio vivo per ruolo

Il bot tiene in vita **un'anteprima** e **un pannello** (menu, domanda, lista, errore):
mandarne uno nuovo **cancella il precedente dello stesso ruolo**. In chat si vede il post
e, se serve, la domanda in corso — non la scia di tutti i tentativi.

```
pannello($testo, $tastiera)   il pannello: ce n'è sempre uno solo
chiudi('anteprima')           cancella (o, se non si può, spegne)
chiudiTutto()                 quando si riparte da zero
avvisa($testo)                un avviso che viaggerà con la prossima anteprima
```

Tre regole che tengono in piedi il meccanismo:

- **I messaggi dell'utente non si toccano mai.** Il bot cancellava anche quelli e spariva
  la cronologia dei link mandati; a lui non servono comunque, il link vive in `$stato['p']`.
- **Si cancella dopo aver costruito il post, non prima.** Se si cancellasse prima, un
  errore di lettura lascerebbe la chat vuota invece che com'era.
- **Oltre le 48 ore Telegram non lascia cancellare.** In quel caso si ripiega sul modo di
  prima — il messaggio resta ma **senza pulsanti** — così non agisce mai su un post vecchio.

Un avviso che accompagna un post (`immagine senza cornice`, `compilato da Amazon`) non è
un messaggio a sé: mandato da solo verrebbe cancellato un istante dopo dall'anteprima.
`avvisa()` lo mette in coda, e l'anteprima lo mostra sotto l'intestazione.

Sopravvive alla pulizia **solo la ricevuta della pubblicazione**: id del post, link al
canale, pulsante per toglierlo. Alla fine, in chat, resta quella riga sola.

### Estrazione: il problema era la pagina delle offerte

Il bot sbagliava quasi sempre a leggere il prodotto. Misurato:

- **`aodAjaxMain`** — la pagina "altre offerte", da cui si leggevano venditori, prezzo e
  condizione — risponde **503 con 314 byte** la maggior parte delle volte. Riprovare
  subito non serve: quattro tentativi a 0/1/2/4 s hanno dato 503 tutte e quattro le volte.
  La pagina prodotto normale, nello stesso momento, rispondeva 200 con 1 MB: **è un limite
  su quell'endpoint, non un blocco della sessione né dell'IP in generale.**
- Il bot ne faceva **due richieste di fila** sullo stesso ASIN: una per mostrare i
  venditori, una quando se ne sceglieva uno. La seconda falliva praticamente sempre.
- Il fallimento era **muto**: `echo ""`, e in chat compariva "Server HTML vuoto".

Cosa si è fatto:

1. **Una sola lettura, in cache.** `allsmid/offerte_amazon.php` è ora l'unico posto che
   legge quella pagina, con una cache di 10 minuti condivisa fra `venditori.php` (che
   mostra la lista) e `infobyscrapallsmid.php` (che compone il post). La scelta del
   venditore non costa più nessuna richiesta ad Amazon.
2. **Un ripiego vero.** Se la lista non è leggibile si usa il buy box della **data API**,
   che non viene limitata. Il post esce lo stesso, con venditore e smid giusti; si perde
   solo la possibilità di scegliere fra i venditori minori, e lo si dice.
3. **Nuovo e usato dalla data API.** Si è verificato che restituisce **due** buying
   options — nuovo *e* usato — ognuna con nome e id del venditore. Prima l'usato si poteva
   avere solo dallo scraping, cioè dalla fonte rotta. Ora "USATO automatico" è affidabile
   quanto il nuovo.
4. **Niente più risposte vuote.** Tutti gli endpoint rispondono `{"errore": "..."}` con il
   motivo, e il bot lo mostra.

### Selettori corretti

| dato | prima | adesso |
|---|---|---|
| prezzo | `.a-price-whole` + `.a-price-fraction`, **servivano entrambi** → 0 se mancava la parte decimale, e l'offerta veniva scartata | `.apex-pricetopay-accessibility-label` (l'etichetta accessibile), con due ripieghi |
| spedizione | `unified-delivery-message-` — id che esiste **solo sulla prima offerta**: dalla seconda in poi risultava sempre gratis | `starts-with(@id,'unified-delivery-message')` |
| venditore | prima `<a>` del blocco — sui siti esteri era il link "politica di reso" | `venditoreDaSoldBy()`, che scarta i link di servizio |
| usato | solo la parola "usato" | anche "confezione aperta/danneggiata" e il magazzino Amazon Seconda mano |

## La tastiera dell'anteprima

Sotto al post ci sono **quattro pulsanti**, non di piu':

```
[ 📤 PUBBLICA ]  [ ✏️ MODIFICA ]
[ ♻️ Rigenera ]  [ 🔄 Nuovo link ]
```

L'anteprima serve a guardare il post: con una dozzina di pulsanti sotto, il post non si
vedeva piu'. **MODIFICA** scambia la tastiera **sullo stesso messaggio** — niente messaggi
nuovi — e apre il pannello con nota, coupon, prezzo, venditore e metodo; **Fine modifiche**
riporta ai quattro.

Il pannello **resta aperto** fra una modifica e l'altra (`$stato['tastiera_aperta']`): chi
sta sistemando un post di solito cambia due o tre cose di fila, e riaprirlo ogni volta
sarebbe un tocco sprecato.

In fondo al pannello i pulsanti sono **due**:

```
[ ⬅️ Fine modifiche ]  [ 📤 PUBBLICA ]
```

L'ultima modifica è quasi sempre l'ultima cosa che si fa prima di pubblicare: chiudere il
pannello per andare a cercare **PUBBLICA** un rigo piu' su era un tocco che non decideva
niente. E' lo stesso pulsante di sopra — stesso `callback_data`, stessa scelta del canale —
non una scorciatoia che salta qualcosa.

## Le rifiniture del post — dentro MODIFICA

| pulsante | cosa fa |
|---|---|
| ✏️ Nota ℹ️ | testo libero fra il titolo e "🏷 Condizione", con una riga vuota sopra e sotto |
| 🎟 Coupon pagina prodotto | chiede prima **% o € fisso**, poi il numero |
| 🗑 Togli coupon | toglie il coupon forzato a mano |
| 🚫 Ignora coupon automatico | spegne il coupon **rilevato da solo**, quando è sbagliato o non c'è. Non su AliExpress |
| ✅/⬜️ 🎫 *codice* | solo AliExpress: il coupon preso dal database, acceso o spento |
| ✅/⬜️ 💳 Paypal / Klarna | solo AliExpress: lo sconto del metodo di pagamento, acceso o spento |
| 🎫 Codice coupon | il codice; se finisce con un numero il bot chiede cosa significa |
| 💰 Prezzo a mano | sovrascrive il prezzo |
| 🛒 Cambia venditore | la lista delle offerte; il link del post punta all'inserzione scelta (`&smid=`) |
| 🎨 Cambia opzione | solo AliExpress: le varianti dell'articolo (1TB/2TB, nero/argento) col loro prezzo |
| ⚙️ Scegli un metodo | le fonti una per una, col loro nome vero |

Coupon forzato e coupon automatico non si sommano: forzarlo significa "è questo".

**Il tipo di coupon si sceglie con un pulsante, non si indovina.** Prima si leggeva dal
testo — `5€` sconto fisso, `5` percentuale — e chi scriveva `5` intendendo cinque euro si
ritrovava un post con il 5% di sconto. Il numero è l'unica cosa che il bot non può
sbagliare da solo; il tipo sì.

**Cambiando venditore o metodo, prezzo e coupon scritti a mano si azzerano**
(`azzeraRifinitureOfferta()`). Descrivevano *quell'* offerta: restando attaccati facevano
uscire il prezzo del venditore di prima sotto il nome di quello nuovo — l'errore più
difficile da vedere, perché il post è perfettamente formato. La **nota** non si tocca: è un
commento di chi scrive, non un dato dell'offerta.

## AliExpress: quale delle opzioni finisce nel post

Su Amazon la stessa pagina la vendono in tanti, e per questo c'è **🛒 Cambia venditore**. Su
AliExpress è il contrario: la stessa pagina contiene **tanti articoli** — l'SSD da 1TB e
quello da 2TB, il nero e l'argento — a prezzi diversi. Nel post ce ne va uno solo.

Se il link dice quale (`sku_id`), si usa quello. **Se non lo dice, si prende il più
economico**: un post ci vuole, e senza nessuna indicazione quello è il ripiego meno
arbitrario. Il problema non era il ripiego, era che avveniva in silenzio: usciva un post
perfettamente formato, con il prezzo giusto — dell'articolo sbagliato. Adesso:

```
👀 ANTEPRIMA
⚠️ Questo articolo ha 6 opzioni e il link non ne indica nessuna: ho preso la più
   economica (1TB a 148,69 €). Se l'offerta è su un'altra: ✏️ MODIFICA → 🎨 Cambia opzione.
```

L'avviso compare **solo quando la scelta l'ha fatta il bot**. Se l'opzione la indica il
link, o l'hai scelta col pulsante, non c'è niente da avvisare. Il pulsante invece resta
sempre, finché le opzioni sono più d'una.

`skuIdDaUrl()` riconosce **le tre forme** in cui AliExpress scrive l'opzione nel link —
`&sku_id=`, il json di `pdp_ext_f`, e la coda `pdp_npi` dei link condivisi dall'app. Prima
ne leggeva una sola, ed è il motivo per cui il ripiego scattava così spesso: due link su
tre l'opzione ce l'avevano, e non veniva vista.

**L'elenco delle opzioni non costa una chiamata in più.** Viaggia insieme al post
(`varianti` nella risposta di `htmlAEbyapi.php`) perché sono gli stessi dati con cui il
post è stato costruito: i prezzi sui pulsanti non possono dissentire da quello scritto
nell'anteprima. Sta in `$stato['varianti']`, e nel `callback_data` — 64 byte — ci va
l'indice, non uno `sku_id` da diciassette cifre.

Cambiando opzione, prezzo e coupon scritti a mano si azzerano come quando si cambia
venditore. I coupon del database **no**: non descrivevano quell'articolo, sono le
promozioni valide oggi su tutto il sito.

## AliExpress: i coupon del database sono già nel post

Coupon e sconti PayPal/Klarna validi si gestiscono in `new/ae_track/coupons.php` e stanno
sulle tabelle `ae_coupon` e `ae_payment_discount`. Erano già lì quando si creava un post a
mano, e li si ridigitava: codice, tipo, importo, tutto di nuovo. Adesso **un link
AliExpress esce già scontato**, e i due sconti si spengono uno per volta:

```
✅ 🎫 SSIT12 -12,00 €          <- premi: diventa ⬜️ e il post torna a 135,69 €
✅ 💳 Paypal 5% · -6,18 €      <- premi: diventa ⬜️, resta solo il coupon
```

Sono **due pulsanti separati** perché sono due cose separate: il coupon si scrive al
check-out, lo sconto PayPal arriva scegliendo come pagare, e capita che valga solo uno dei
due. Il segno di spunta dice se in questo momento è dentro al post; la percentuale porta
accanto anche quanto pesa in euro **su questo prezzo**, che è il numero su cui si decide.

**Il conto lo fa l'endpoint, non il bot.** `htmlAEbyapi.php?sconti_db=1` chiama
`aeScontiDbPerPost()` (in `ae_track/ae_utility.php`, dove sta già tutta la logica sconti),
applica, e **riporta nella risposta** che cosa ha applicato. Il bot tiene solo tre campi —
`sconti_db` (accesi), `ae_off_coupon`, `ae_off_pay` (i due interruttori) — e ricopia le
righe in `ae_sconti` per scriverci sopra i pulsanti. Il motivo è che il prezzo ce l'ha solo
l'endpoint: un conto rifatto in due posti prima o poi diverge, e su un prezzo divergere
vuol dire pubblicare un numero falso. Ogni pulsante rigenera comunque il post, quindi non
costa niente lasciargli fare il conto ogni volta.

Due regole che il conto deve rispettare, e che è il motivo per cui non è una sottrazione:

- **l'idoneità si giudica sul totale dell'ordine** (spedizione inclusa: è quello che si
  paga, ed è quello a cui si riferisce «ordini 149€+»), **l'importo sul prezzo che il post
  mostra** (la spedizione non ce l'ha). Con un unico numero per entrambi esce uno sconto
  che al check-out non c'è, o che c'è più grande di com'è scritto;
- **lo sconto pagamento si calcola su quel che resta dopo il coupon.** Per questo spegnere
  il coupon rialza il PayPal — 5% di 135,69 non è 5% di 123,69 — e per questo i due
  pulsanti non hanno bisogno di sapere niente l'uno dell'altro: rifà tutto l'endpoint.

Quando lo sconto è **solo** quello del pagamento, il post lo mostra come coupon col prezzo
finale (`PAYPAL`, 128,91 €): il template non ha un posto dove appendere uno sconto "extra"
senza un coupon davanti. È la stessa scelta che fa `aeSendOffer()` per le offerte del
tracker, quindi i due tipi di post si assomigliano invece di contraddirsi.

**Quello che si scrive a mano vince sempre.** 🎫 Codice coupon, 💰 Prezzo a mano e 💳 Coupon
+ PayPal/Klarna chiamano `scordaScontiDb()`: gli sconti del database escono dal post e i
loro due pulsanti spariscono, perché offrire di spegnere qualcosa che non c'è più è peggio
che non offrire niente. Per riaverli si reincolla il link.

Su AliExpress **non compare** 🚫 *Ignora il coupon rilevato in automatico*: lì non spegne
niente, perché `htmlAEbyapi.php` non legge `no_coupon`. Un pulsante che non fa niente,
accanto a due che fanno esattamente quel che dicono, toglie fiducia anche a loro.

## La pubblicazione: niente conferma, ma si puo' disfare

Scegliere il canale **pubblica**. Il passo "Confermi la pubblicazione?" c'era ed e' stato
tolto: scegliere il canale e' gia' un atto deliberato, e un secondo tocco che dice sempre
di si' smette presto di essere letto.

Al suo posto c'e' qualcosa che serve davvero — il rimedio invece della prevenzione.
Pubblicato il post, anteprima ed elenco dei canali **spariscono** e resta la ricevuta:

```
📣 Pubblicato su 📢 offerte · #12345
[ 👁 Vedi ] [ 🗑 Elimina ] [ ✏️ Modifica ]
[ 📤 Altro canale ] [ 🆕 Nuovo post ]
```

"Vedi" e' un link `t.me/c/<canale>/<messaggio>`: i canali privati non hanno un @nome, ma
quell'indirizzo li apre a chi e' dentro — e chi usa il bot ci sta dentro.

Il bot e' amministratore del canale (ci pubblica), quindi puo' cancellare quello che ha
mandato. **Telegram non lo permette oltre le 48 ore**: in quel caso il motivo viene
riportato invece di far finta che sia andata.

Due dettagli che sembrano minori e non lo sono:

- **Canale e id del messaggio stanno dentro il `callback_data`** (`del:<canale>:<msgid>`),
  non nello stato. Cosi' il pulsante funziona anche fra una settimana, quando lo stato e'
  stato azzerato dieci volte. Sono ~28 byte, il limite di Telegram e' 64.
- La ricevuta **non passa da `pannello()`**. Se fosse il pannello, la pubblicazione
  successiva la cancellerebbe insieme al suo pulsante di eliminazione — cioe' proprio
  quello che deve restare a disposizione.

## Il post personalizzato

Il link si chiede **per primo**, e non per ultimo come prima: è l'unico dato da cui si
possono ricavare gli altri. Sapendolo, il bot riconosce il sito (anche estero), lo reffa,
e può offrire **📥 Compila da Amazon** per titolo, prezzo, venditore e immagine.

### "Compila da Amazon" non si arrende più

Tre cose che prima finivano tutte nella stessa frase, *"non sono riuscito a leggere i
dati"*:

1. **Due fonti invece di una.** `get_products.php` interroga AAPI e, se quella non
   conosce l'ASIN **o non risponde affatto**, ripiega da sola sulla **Creators API**, che
   ha credenziali sue e non dipende dalle vetrine. Il 04/08/2026 le vetrine hanno
   risposto 429 per ore: con il ripiego il post si crea lo stesso.
2. **I dati parziali si tengono.** Bastava che mancasse il titolo per buttare via anche
   prezzo e immagine. Ora si riempie quello che arriva e si chiede **solo il resto**
   (`continuaCustom()`), con il riepilogo di cosa c'è e cosa manca in testa alla domanda.
3. **Il motivo si dice.** «l'ASIN non esiste su amazon.it», «marketplaceId sconosciuto
   per 'com'»: si capisce se rifare il link o scrivere a mano, invece di indovinare.

### Lo smid a mano

`🆔 Smid` nel pannello MODIFICA (e nella domanda sul venditore). Il nome del venditore è
una scritta nel post; **lo smid decide a quale inserzione punta il link**. Senza, il link
va all'offerta in evidenza — che può essere di un altro venditore rispetto a quello
scritto nel post.

Tre difetti sistemati prima:

1. **Il link non veniva reffato.** Finiva nel post tale e quale: nessun tag di
   affiliazione. Ora Amazon passa da `createRefAmazonLink()` (tag del canale + smid +
   shortlink), AliExpress da `generateAffiliateLink()`, Esseshop prende i suoi parametri.
2. **L'immagine non entrava nel template.** Veniva passato il `file_id` di Telegram, che
   non è un indirizzo: il generatore della cornice non poteva aprirlo, e il post usciva
   con la foto nuda senza prezzo. Ora il file viene salvato in `uploads/` e si passa il
   suo URL pubblico.
3. **Gli allegati non erano accettati.** Ora sì, ed è la via consigliata: Telegram non
   ricomprime i `document`, quindi un PNG arriva intatto. Tutto viene poi ricodificato in
   JPEG su fondo bianco — lo stesso della cornice — così anche WEBP e PNG con trasparenza
   sono sicuri di essere leggibili dal generatore.

## Le fonti, chiamate col loro nome

I pulsanti dicevano "NUOVO automatico", "API Amazon (tutti)", "API Amazon (usato)": non
si capiva da dove arrivasse il dato, quindi quando un post usciva sbagliato non si sapeva
né chi incolpare né cosa provare al suo posto. Adesso portano il nome della sorgente, lo
stesso che compare negli errori:

| pulsante | endpoint | cosa la distingue |
|---|---|---|
| **Backend API** — nuovo / usato | `html/htmlbydataapi.php` → `backend_data_api/infoprodotto.php` | `data.amazon.<sito>`. La predefinita: non viene limitata, dà venditore e smid |
| **Api Cart** — nuovo / usato | `html/htmlbyapicart.php` → `rx/infoprodotto_carrello.php` | cerca l'offerid del **nuovo di Amazon** (se Amazon non ce l'ha: l'offerta in evidenza, con avviso) o dell'**usato** (con gradazione), lo prova nel carrello e il post esce **sempre col FAST CHECKOUT** su quell'offerta. Nessun ripiego sulla Creators: l'offerid non lo darebbe |
| **Creators API** — nuovo / usato / solo Amazon | `html/htmlbyapi.php` | l'API ufficiale di affiliazione: risponde anche su ASIN che la prima non conosce |
| **Juvec** — nuovo | `html/htmlbyjuvec.php` | la vecchia fonte, solo nuovo |
| **Pagina offerte** — nuovo / usato | `allsmid/infobyscrapallsmid.php` | scraping di tutte le offerte: è quella che Amazon blocca più spesso |

**Tutte e due le API leggono anche l'usato.** Della Backend API mancava proprio la riga —
c'era solo il "NUOVO automatico" — e della Creators non si capiva che `API Amazon (usato)`
fosse lei.

## Se la fonte non risponde, ci prova la Creators API

Quando il metodo scelto fallisce, il bot **non si arrende subito**: rifà la richiesta alla
Creators API e, se quella risponde, il post esce lo stesso — con scritto in anteprima
perché:

```
⚠️ Post creato con la Creators API perche' Backend API (nuovo) non ha risposto.
```

Funziona perché le due fonti non condividono niente: la Creators ha credenziali proprie e
non dipende né dalla sessione Amazon né dalle vetrine del token. Il 04/08/2026
`data.amazon` ha risposto 503 per ore e il bot non creava più un post; con il ripiego
sarebbe uscito lo stesso.

`ripiegoCreators()` decide in base al **metodo chiesto**, non all'errore:

| metodo | ripiego |
|---|---|
| Backend API nuovo / Juvec | Creators API nuovo |
| Backend API usato | Creators API usato |
| Creators API | — è già lei |
| Pagina offerte (venditore scelto) | — **mai** |

L'ultima riga è la più importante: chi ha scelto un venditore preciso vuole *quel*
venditore, e un post costruito sul buy box di un altro sarebbe una risposta sbagliata data
con sicurezza. Meglio l'errore.

Se falliscono entrambe, il messaggio riporta il motivo della fonte chiesta **e** quello
del ripiego, così si vede subito se è un problema del prodotto o dell'infrastruttura.

## Le chiamate non escono più dalla macchina

Gli endpoint stanno sullo stesso server del bot, ma la richiesta usciva sull'**IP
pubblico** e rientrava: due attraversate di firewall e NAT per parlare con il processo
della porta accanto. Ora la connessione va a `127.0.0.1` con `CURLOPT_RESOLVE`, tenendo
l'IP pubblico nell'header `Host`: Apache sceglie **lo stesso vhost di prima**, quindi non
cambia cosa risponde — cambia solo la strada.

Se in locale non risponde (connessione rifiutata, o 404 perché lì quel percorso non è
servito) si riprova una volta per la via lunga. Solo in quei due casi, che sono gli unici
in cui l'endpoint **non ha girato**: su un 500 o su un errore applicativo non si ritenta,
perché certi endpoint scrivono a database e li si farebbe lavorare due volte.

## Il prezzo barrato quando la fonte non ce l'ha

La pagina "tutte le offerte" (`scrap_all_smid`) dà prezzo, condizione e venditore, ma
**nessun prezzo di riferimento**: il post usciva col solo prezzo, senza niente da cui
scontare — proprio nel caso in cui si è scelto il venditore a mano.

Ora ci pensa `riferimento30Giorni()` (`new/core/utility.php`), chiamata da
`createTextAndImageByData()` quando la fonte non passa un barrato: **la media 30 giorni
del nostro storico** (`amazon_alert_report`), presa **sulla stessa condizione**.

- usato → media dell'usato. Se non c'è, **nessun riferimento**: ripiegare sulla media del
  nuovo mostrerebbe un usato scontato del 40% sul prezzo di un altro oggetto;
- terzo venditore → media dei terzi, altrimenti quella generica (è lo stesso prodotto
  nuovo);
- sotto il **+5%** si risponde "niente": un barrato appiccicato al prezzo si legge come un
  errore di battitura, non come un affare.

## Cose da sapere

- **`uploads/` dev'essere scrivibile da `www-data`.** Il bot la crea da solo; se non ci
  riesce, il post esce comunque con la foto senza cornice e lo dice. Rimedio:
  `chmod 775 uploads && chgrp www-data uploads`.
- **`setup_menu.php` va lanciato a mano** dopo il primo deploy e ogni volta che si
  cambiano i comandi. Non sta nel webhook di proposito: `setMyCommands` è
  un'impostazione del bot, non una risposta a un messaggio.
- **Gli update duplicati sono filtrati.** Telegram rimanda lo stesso update se il webhook
  non risponde in tempo, e leggere un'offerta può superare il suo timeout: senza il
  filtro, nel caso peggiore il post veniva pubblicato due volte.
