Profile 1 is correct — leave it alone. Profile 2 has 10 steps and should have 13. Three things were meant to go in when the Write-File steps came out, and never did. That is why one reply arrives up to three times. Add steps 6, 10, 12 and one line in step 2.
?Why the duplicate happens
The worker hands your phone a reply and keeps its own copy, waiting for the phone to say “sent.”
Your phone never says it — that is the missing /outbox/ack step. So ten minutes later the
worker decides the send must have failed and puts the reply back on the queue. Your phone sends it again.
That happens twice, then the worker gives up and logs a drop.
One reply, sent three times. Adding step 12 ends it.
0 · Put your keys in
Profile 1 — “BOT REPLY” (task 9) · ✅ 5 steps · CHECK ONLY
%antitle and %antext, and the POST is built from both.1Stop — throw away non-texts
| Action | Task → Stop |
| Fields | all blank |
| If | %anpackage Doesn’t Match com.android.mms |
| Continue Task After Error | ☐ UNTICKED — should |
Without it the bot answers your bank.
2Stop — the title must be a number
| Action | Task → Stop |
| If | %antitle Doesn’t Match Regex [0-9] |
| Continue Task After Error | ☐ UNTICKED — should |
A saved contact fires the title “Dave”. The reply would queue to the word Dave and never send. Keep this guard.
3Variable Search Replace — escape line breaks
| Action | Variables → Variable Search Replace |
| Variable | %antext |
| Search | \n |
| Replace With | \\n |
| Replace Matches | ☑ TICKED |
| Ignore Case / Multi-Line | ☐ ☐ both unticked |
| One Match Only | ☐ UNTICKED — must (two line breaks need both escaped) |
| Continue Task After Error | ☑ TICKED — should |
A raw line break inside a JSON string is invalid JSON — the worker rejects the whole POST and he gets silence. Ticked, because a message with no line breaks must not kill the send.
4HTTP Request — the message goes out
| Method | POST |
| Query Parameters | blank |
| File To Send | blank |
| File/Directory To Save With Output | blank |
| Timeout | 30 |
| Trust Any Certificate | ☐ UNTICKED — must |
| Automatically Follow Redirects | ☑ ticked (default, harmless) |
| Structure Output (JSON) | ☐ UNTICKED — should, nothing reads the reply here |
| Continue Task After Error | ☐ UNTICKED — must |
The operator key has no default. Missing or wrong, the worker answers nothing — and both look identical from the outside: total silence.
5AutoNotification Cancel
| Action | Plugin → AutoNotification → Cancel |
| Notification Key | %ankey |
| Cancel All / Cancel Persistent | ☐ ☐ both unticked |
| Continue Task After Error | ☑ TICKED — should |
The message is already safe at the worker. Failing to tidy up a notification must never look like a failed send.
Profile 2 — “2BOT REPLY” (task 12) · ⚠️ 10 → 13 steps
Add the new steps in reverse — 12, then 10, then 6, then the line in step 2. Inserting a step pushes everything below it down, so working bottom-up means these numbers never shift under you.
1HTTP Request — collect the replies
| Method | GET |
| Headers / Body | blank |
| Timeout | 30 |
| Trust Any Certificate | ☐ UNTICKED — must |
| Structure Output (JSON) | leave exactly as you have it — should |
| Continue Task After Error | ☐ UNTICKED — must |
Step 2 reads the raw text either way, so your current Structure Output setting works. Don’t change a working step while fixing a different one.
2JavaScriptlet — 🆕 ADD ONE LINE
var i = []; with the other declarations · i.push(m[k].id); inside the loop ·
the setLocal('IDS', …) line at the bottom.| Auto Exit | ☑ ticked |
| Timeout | 45 (default — leave it) |
| Libraries | blank |
| Continue Task After Error | ☐ UNTICKED — must |
local('http_data')— neverglobal(). With global the count never sets at all.- Join on
@@@— never\n. Reply bodies contain line breaks and would be shredded. - The
idis already arriving from the worker. You are not asking for anything new.
If this throws, the count keeps its old value and the loop below re-sends the last batch — hence unticked.
3Stop ⚠️ GUARD — the count must be real
| Action | Task → Stop |
| If | %MCOUNT Doesn’t Match Regex ^[1-9][0-9]*$ |
| Continue Task After Error | ☐ UNTICKED — should |
Guard one of three. Nothing runs unless there is a whole number of 1 or more waiting.
4Variable Split — %PHONES
| Name | %PHONES |
| Splitter | @@@ |
| Delete Base | ☐ UNTICKED — must |
| Regex | ☐ UNTICKED — must |
| Continue Task After Error | ☐ UNTICKED — must |
A failed split leaves the previous run’s numbers in place — texts to the wrong men.
5Variable Split — %BODIES
Identical to step 4 in every field and every checkbox, except Name = %BODIES.
The reply texts.
6Variable Split — 🆕 ADD THIS — %IDS
| Action | Variables → Variable Split |
| Name | %IDS |
| Splitter | @@@ |
| Delete Base | ☐ UNTICKED |
| Regex | ☐ UNTICKED |
| Continue Task After Error | ☐ UNTICKED — must |
The receipt numbers, one per reply, matching PHONES and BODIES position for position. If this split silently fails, step 12 would tell the worker to delete the wrong reply — one that never sent.
7For ⚠️ GUARD — the counted loop
| Action | Task → For |
| Variable | %idx |
| Items | 1:%MCOUNT |
| Continue Task After Error | ☐ UNTICKED — should |
Guard two. A fixed, counted loop. No raw Goto. Ever. A Goto is what caused the runaway that fired thousands of junk sends.
8Variable Set — %num
| Name | %num |
| To | %PHONES(%idx) |
| Recurse Variables | ☐ unticked |
| Do Maths | ☐ UNTICKED — must, a phone number is not a sum |
| Append | ☐ UNTICKED — must, ticked would glue each number onto the last |
| Structure Output (JSON) | ☐ unticked |
| Continue Task After Error | ☐ UNTICKED — must |
9Variable Set — %msg
Identical to step 8 in every field and every checkbox, except Name = %msg
and To = %BODIES(%idx).
10Variable Set — 🆕 ADD THIS — %mid
| Action | Variables → Variable Set |
| Name | %mid |
| To | %IDS(%idx) |
| Do Maths | ☐ UNTICKED — must |
| Append | ☐ UNTICKED — must |
| Continue Task After Error | ☐ UNTICKED — must |
Append ticked would make pass 2 send both ids glued together; the worker would strip it to gibberish and delete nothing.
11Send SMS ⚠️ GUARD — check this checkbox carefully
| Action | Phone → Send SMS |
| Number | %num |
| Message | %msg |
| Store In Messaging App | leave as you have it — your call, it doesn’t affect delivery |
| SIM Card | leave as set |
| If | %num Matches Regex ^\+?[0-9][0-9 ]{7,}$ |
| Continue Task After Error | ☐ UNTICKED — MUST |
Unticked: a failed send stops the task right here, step 12 never runs, no ACK reaches the worker, the reply goes back on the queue and the phone tries again. Nothing is lost.
Ticked: a failed send falls straight through to step 12, which tells the worker “sent — delete it.” The worker deletes it. He gets silence and there is no record it ever existed. That is worse than the duplicate bug you have now.
Guard three: the number must look like a phone number before a single SMS leaves the handset.
12HTTP Request — 🆕 ADD THIS — THE ACK
| Method | POST |
| Query Parameters | blank — the key is already in the URL |
| File To Send / Save Output | blank |
| Timeout | 30 |
| Trust Any Certificate | ☐ UNTICKED — must |
| Automatically Follow Redirects | ☑ ticked (default) |
| Structure Output (JSON) | ☐ UNTICKED — should |
| If | %num Matches Regex ^\+?[0-9][0-9 ]{7,}$ |
| Continue Task After Error | ☑ TICKED — must, and deliberately the opposite of step 11 |
forbidden back and deletes nothing.Tells the worker the text actually left the handset, so it can delete its copy. This step is the whole fix.
13End For
| Action | Task → End For |
| Continue Task After Error | ☐ unticked |
Closes the loop opened at step 7.
The three guards — never remove any of them
| Profile 2, step 3 | the count regex Stop — stops the loop running on junk |
| Profile 2, step 7 | the For’s fixed count — no raw Goto, ever |
| Profile 2, step 11 | the Send SMS number regex — nothing texts a non-number |
Order to do it in
- Run the deploy on the laptop first. The ACK route has to be live before step 12 can do anything — added early it just gets a silent 404.
- Then make the four changes to Profile 2 (about 2 minutes).
- Then test, below.
How to test — without texting a real person
- Text the bot number from a second handset.
- Wait up to 2 minutes — Profile 2 only runs on its timer.
- You should get exactly one reply.
- Wait a further 15 minutes and check nothing arrives a second time. That is the real proof the ACK works — the duplicate only ever appeared after the worker’s 10-minute clock ran out.
When something breaks — logging, temporarily
- Re-add a Write File step → File
Download/bot-debug.txt→ tick Append → on whichever profile is misbehaving - Reproduce the problem once
- Open
Download/bot-debug.txtin Chrome and send the text - Remove the step again
Things that look like faults but aren’t
| An identical message from the same number is ignored for 45 minutes | Only if it is 12+ characters and the bot already answered him once. Otherwise the window is 2 minutes. This exists because notifications re-fired the same message. |
| A qualified number goes quiet for 12 hours | He is yours now, not the bot’s. |
?sync=1 reports a fragment error |
It lies — it bypasses the ordering room. Test the plain POST. |
| Reading the outbox URL empties it | It is a take, not a peek. Never open it during a live test. |