Wiring the bot phone

Two Tasker profiles. Nothing clever on the phone — the brain is online.

What is wrong right now
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

Paste from your QR pack and every code box below fills itself in. Nothing leaves this page — no network call, no storage.
operator_key
outbox_key

Profile 1 — “BOT REPLY” (task 9) · ✅ 5 steps · CHECK ONLY

Do not change this profile. Open each step and confirm it matches. Trigger stays AutoNotification Intercept — not Tasker’s own “Received Text”. The intercept is what gives you %antitle and %antext, and the POST is built from both.
1Stop — throw away non-texts
ActionTask → Stop
Fieldsall blank
If%anpackage Doesn’t Match com.android.mms
Continue Task After Error☐ UNTICKEDshould

Without it the bot answers your bank.

2Stop — the title must be a number
ActionTask → Stop
If%antitle Doesn’t Match Regex [0-9]
Continue Task After Error☐ UNTICKEDshould

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
ActionVariables → Variable Search Replace
Variable%antext
Search\n
Replace With\\n
Replace Matches☑ TICKED
Ignore Case / Multi-Line☐ ☐ both unticked
One Match Only☐ UNTICKEDmust (two line breaks need both escaped)
Continue Task After Error☑ TICKEDshould

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
MethodPOST
URL
https://elevate-bot-lab.elevate-emc.workers.dev/
Headers
Content-Type: application/json
Body
{"from":"%antitle","text":"%antext","operator_key":"PASTE_YOUR_OPERATOR_KEY_ABOVE"}
Query Parametersblank
File To Sendblank
File/Directory To Save With Outputblank
Timeout30
Trust Any Certificate☐ UNTICKEDmust
Automatically Follow Redirects☑ ticked (default, harmless)
Structure Output (JSON)☐ UNTICKEDshould, nothing reads the reply here
Continue Task After Error☐ UNTICKEDmust
Unticked so that a failed POST stops the task before step 5 cancels the notification. AutoNotification then re-fires it and you get a second chance. Ticked, a failed POST eats his message forever.
Never tick Trust Any Certificate — that turns off certificate checking on the connection carrying your clients’ messages.

The operator key has no default. Missing or wrong, the worker answers nothing — and both look identical from the outside: total silence.

5AutoNotification Cancel
ActionPlugin → AutoNotification → Cancel
Notification Key%ankey
Cancel All / Cancel Persistent☐ ☐ both unticked
Continue Task After Error☑ TICKEDshould

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

Trigger: Time, every 2 minutes (Tasker’s floor).
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
MethodGET
URL
https://elevate-bot-lab.elevate-emc.workers.dev/outbox?key=PASTE_YOUR_OUTBOX_KEY_ABOVE
Headers / Bodyblank
Timeout30
Trust Any Certificate☐ UNTICKEDmust
Structure Output (JSON)leave exactly as you have itshould
Continue Task After Error☐ UNTICKEDmust
If this GET fails, the variable still holds the previous run’s replies. Continuing would parse them again and re-send messages that already went. Ticking this creates duplicates on its own.
Never open this URL in a browser during a live test. Reading it takes the messages — your phone then finds an empty queue and he gets nothing.

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
If your existing block works, do NOT paste over it. Add three things to what’s there: var i = []; with the other declarations · i.push(m[k].id); inside the loop · the setLocal('IDS', …) line at the bottom.
var d = JSON.parse(local('http_data')); var m = d.messages || []; var p = [], b = [], i = []; for (var k = 0; k < m.length; k++) { p.push(m[k].phone); b.push(m[k].body); i.push(m[k].id); } setLocal('MCOUNT', String(m.length)); setLocal('PHONES', p.join('@@@')); setLocal('BODIES', b.join('@@@')); setLocal('IDS', i.join('@@@'));
Auto Exit☑ ticked
Timeout45 (default — leave it)
Librariesblank
Continue Task After Error☐ UNTICKEDmust
  • local('http_data')never global(). With global the count never sets at all.
  • Join on @@@never \n. Reply bodies contain line breaks and would be shredded.
  • The id is 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
ActionTask → Stop
If%MCOUNT Doesn’t Match Regex ^[1-9][0-9]*$
Continue Task After Error☐ UNTICKEDshould

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☐ UNTICKEDmust
Regex☐ UNTICKEDmust
Continue Task After Error☐ UNTICKEDmust

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
ActionVariables → Variable Split
Name%IDS
Splitter@@@
Delete Base☐ UNTICKED
Regex☐ UNTICKED
Continue Task After Error☐ UNTICKEDmust

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
ActionTask → For
Variable%idx
Items1:%MCOUNT
Continue Task After Error☐ UNTICKEDshould

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☐ UNTICKEDmust, a phone number is not a sum
Append☐ UNTICKEDmust, ticked would glue each number onto the last
Structure Output (JSON)☐ unticked
Continue Task After Error☐ UNTICKEDmust
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
ActionVariables → Variable Set
Name%mid
To%IDS(%idx)
Do Maths☐ UNTICKEDmust
Append☐ UNTICKEDmust
Continue Task After Error☐ UNTICKEDmust

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
ActionPhone → Send SMS
Number%num
Message%msg
Store In Messaging Appleave as you have it — your call, it doesn’t affect delivery
SIM Cardleave as set
If%num Matches Regex ^\+?[0-9][0-9 ]{7,}$
Continue Task After Error☐ UNTICKEDMUST
The single most important checkbox in the whole setup.
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
MethodPOST
URL
https://elevate-bot-lab.elevate-emc.workers.dev/outbox/ack?key=PASTE_YOUR_OUTBOX_KEY_ABOVE
Headers
Content-Type: application/json
Body
{"id":"%mid"}
Query Parametersblank — the key is already in the URL
File To Send / Save Outputblank
Timeout30
Trust Any Certificate☐ UNTICKEDmust
Automatically Follow Redirects☑ ticked (default)
Structure Output (JSON)☐ UNTICKEDshould
If%num Matches Regex ^\+?[0-9][0-9 ]{7,}$
Continue Task After Error☑ TICKEDmust, and deliberately the opposite of step 11
Why ticked here: if the ACK itself fails, the text has already been sent. Stopping the task would abandon the other replies in this batch. Ticking it lets the loop finish — the worst case is one reply sending twice, instead of four other men getting nothing at all.
This uses the outbox key, same as step 1 — not the operator key. A wrong key gets 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
ActionTask → 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 3the count regex Stop — stops the loop running on junk
Profile 2, step 7the For’s fixed count — no raw Goto, ever
Profile 2, step 11the Send SMS number regex — nothing texts a non-number

Order to do it in

  1. 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.
  2. Then make the four changes to Profile 2 (about 2 minutes).
  3. Then test, below.

How to test — without texting a real person

Use an ACMA fictitious number only. These are reserved by law and can never reach anybody:
0491 570 006 → 0491 570 156
Never test on a real number.
  1. Text the bot number from a second handset.
  2. Wait up to 2 minutes — Profile 2 only runs on its timer.
  3. You should get exactly one reply.
  4. 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

  1. Re-add a Write File step → File Download/bot-debug.txt → tick Append → on whichever profile is misbehaving
  2. Reproduce the problem once
  3. Open Download/bot-debug.txt in Chrome and send the text
  4. Remove the step again
Not a bug: odd characters where a dash should be, in the debug file. The real SMS renders it correctly. Don’t “fix” it.

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.