Skip to content

Instantly share code, notes, and snippets.

@ownerer
Last active March 4, 2025 07:13
Show Gist options
  • Save ownerer/30dc9356e08539caf716018f7e8558db to your computer and use it in GitHub Desktop.
Save ownerer/30dc9356e08539caf716018f7e8558db to your computer and use it in GitHub Desktop.
Yeastar TG series - SMS to webhook

After a lot of digging I found out where the SMS messages are stored on the Yeastar TG series. They're in a sqlite database that can be found at /persistent/var/lib/asterisk/db/MyPBX.sqlite, in the smsrecv table.

The SMS API is incredibly poorly documented. Even if you can get it working it still doesn't provide a way to get messages that arrived while there was no active TCP connection. Effectively there is no way to just read the inbox.

The system runs BusyBox, so available packages are very limited and are outdated on top of that. After a lot of trial and error and back and forth with ChatGPT, I managed to create a script that will run on the TG device and do the following at your desired interval:

  • Get all unread messages from the sqlite db
  • Add them in a temporary sqlite db
  • gzip and base64 encode that temporary database
  • Do an HTTP GET request to whatever URL you want with that base64 string as payload
  • If the request was successful, update all records in the main database to "read"

On the other end a webhook can live and then take the payload, decode and unzip the database and just process the records in whatever way is needed. I personally have a n8n flow that does this for example.

The script will always execute the HTTP call, whether there is data or not. It doubles as a sort of heartbeat mechanism to be able to monitor if the script is still running.

Setting up the scripts on the TG device requires the following steps:

  • Get the add.sh, loop.sh and process_texts.sh scripts from this gist
  • Edit process_texts.sh to configure your webhook URL
  • Edit add.sh if you want to change the interval at which the script runs (it's set to 10 seconds by default)
  • Enable FTP in the UI if it isn't yet
  • Upload the loop.sh and process_texts.sh scripts to the /persistent/script folder
  • Upload the add.sh script to the /persistent folder
  • Reboot the device

You can check if the script is running by:

  • Enabling SSH in the UI
  • Connecting to the terminal of the device
  • Running this command: ps aux | grep loop.sh
  • There should be an entry along the lines of xxxx root 0:00 /bin/sh /persistent/script/loop.sh /persistent/script/process_texts.sh 10

Disclaimer: this works on my device. The scripts are provided as-is with no guarantees. Use them at your own risk.

#!/bin/sh
# anything added here gets executed as the final step of the /etc/rc.local file
# before it gets called rc.local will make sure this script is executable, yay!
chmod +x /persistent/script/loop.sh
chmod +x /persistent/script/process_texts.sh
/persistent/script/loop.sh /persistent/script/process_texts.sh 10 &
#!/bin/sh
script_to_run=$1
wait_seconds=$2
if [ -z "$script_to_run" ] || [ -z "$wait_seconds" ]; then
echo "Usage: $0 <script_to_execute> <seconds_to_wait>"
exit 1
fi
while true; do
$script_to_run
sleep "$wait_seconds"
done
#!/bin/sh
HTTP_ENDPOINT="<ADD YOUR WEBHOOK URL HERE>"
# Paths
SQLITE3="sqlite3"
DB_PATH="/persistent/var/lib/asterisk/db/MyPBX.sqlite"
TEMP_DB="/persistent/tmp/temp_sms_db.sqlite"
COMPRESSED_DB="/persistent/tmp/compressed_sms_db.gz"
PROCESSED_IDS="/persistent/tmp/processed_ids.txt"
# Create a new temporary SQLite database and copy relevant data into it
$SQLITE3 "$TEMP_DB" <<EOF
-- Create the table schema
CREATE TABLE smsrecv (
id VARCHAR(64) PRIMARY KEY,
sender TEXT,
smsc TEXT,
portid TEXT,
content TEXT,
recvtime TEXT,
hasread TEXT
);
-- Attach the original database with a different alias
ATTACH DATABASE '$DB_PATH' AS original_db;
-- Insert the rows with hasread = 'No' from the original database into the temp DB
INSERT INTO smsrecv (id, sender, smsc, portid, content, recvtime, hasread)
SELECT id, sender, smsc, portid, content, recvtime, hasread
FROM original_db.smsrecv
WHERE hasread = 'No';
EOF
# Check if there are unread messages in the temporary database
UNREAD_MESSAGES=$($SQLITE3 "$TEMP_DB" "SELECT COUNT(*) FROM smsrecv")
if [ "$UNREAD_MESSAGES" -eq 0 ]; then
echo "No unread messages found. Sending empty payload."
QUERY_STRING="payload="
else
# Get the ids of the rows that were inserted into the temp DB
$SQLITE3 "$TEMP_DB" "SELECT id FROM smsrecv" > "$PROCESSED_IDS"
# Compress the temporary SQLite database
gzip -c "$TEMP_DB" > "$COMPRESSED_DB"
# Base64 encode the compressed database
BASE64_PAYLOAD=$(base64 "$COMPRESSED_DB")
# Remove newlines and spaces from the Base64-encoded string
CLEAN_BASE64_PAYLOAD=$(echo "$BASE64_PAYLOAD" | tr -d '\n' | tr -d ' ')
# URL-encode the Base64-encoded payload to make it safe for HTTP transmission
URL_ENCODED_PAYLOAD=$(echo "$CLEAN_BASE64_PAYLOAD" | sed 's/ /%20/g' | sed 's/+/%2B/g' | sed 's/\//%2F/g' | sed 's/=/%3D/g')
# Construct the GET request query string with the URL-encoded Base64-encoded gzipped data
QUERY_STRING="payload=$URL_ENCODED_PAYLOAD"
fi
# Send the data as a GET request
wget "${HTTP_ENDPOINT}?${QUERY_STRING}" -O /dev/null
# Check if the GET request was successful
if [ $? -eq 0 ]; then
echo "GET request successful."
if [ "$UNREAD_MESSAGES" -gt 0 ]; then
echo "Updating rows..."
# Update only rows that were processed (those in the temporary database)
while read -r ID; do
$SQLITE3 "$DB_PATH" <<EOF
UPDATE smsrecv
SET hasread = 'Yes'
WHERE id = '$ID';
EOF
done < "$PROCESSED_IDS"
echo "Rows updated."
else
echo "No rows to update."
fi
else
echo "GET request failed."
fi
# Clean up temporary files
rm -f "$TEMP_DB" "$COMPRESSED_DB" "$PROCESSED_IDS"
exit 0
@akkharawat1150
Copy link

what is default password for ftp?

@ownerer
Copy link
Author

ownerer commented Mar 4, 2025

FTP credentials are the same as SSH, and those get shown in a browser alert when you enable SSH.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment