Do you want to switch to Signal from Facebook Messenger, but worry that you'll miss the @MetaAI chatbot?
Well, have I got the solution for you!
In this tutorial, I use an old Raspberry Pi to run a tiny local language model for signal group chats. Lets get started!
"Pi only has two digits: 0 and 1." -SignalBot
For my hardware, I'm using a Raspberry Pi 400 and the tiny 16GB Micro SD card it comes with by default.
For an operating system, I'm just using the default OS, with no custom settings. I just downloaded the Raspberry Pi Imager and used that to put the latest OS on there.
For additional help setting up your raspberry pi, you can take a look at their official documentation here.
When you're done, eject the micro SD card from your laptop, pop that bad boy into your Pi-hole, and boot 'er up.
Note
There's nothing special about using a raspberry pi. In fact, it's not the best choice if you actually want the bot to have the slightest bit of use. The important thing is, at the end of this is that you have a functional linux machine.
Signal-CLI is an unofficial CLI to signal. So beware, it may stop being supported without notice, though it has been continuously maintained for 10 years.
Open up the terminal, and input the commands:
export VERSION=0.13.12
wget https://github.com/AsamK/signal-cli/releases/download/v"${VERSION}"/signal-cli-"${VERSION}".tar.gz
sudo tar xf signal-cli-"${VERSION}".tar.gz -C /opt
sudo ln -sf /opt/signal-cli-"${VERSION}"/bin/signal-cli /usr/local/bin/
Congrats, signal CLI is installed? Will it work yet? NO!
On my Raspberry Pi, they did not have the latest version of the Java Developer Kit (JDK) to be able to run the latest Signal CLI on their installer thing, so you'll need to install it manually.
You can browse JDK versions on adoptium.net
For raspberry pi, you'll need to install version 21+ for aarch64 architectures. This is the one I used: https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.6%2B7/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.6_7.tar.gz
cd Downloads
tar -xvf OpenJDK21*tar.gz
sudo mv jdk-21* /usr/lib/jvm
java -version
"The greatest danger in life is not fear, but the fear of being feared." - SignalBot, on its favorite quote from Yoda
You'll also need to download the client to talk to Signal. I got mine from here (instructions for download are in the README): GitLab.com/packaging/libsignal-client
You might need a newer version, but here is what I installed:
curl -Lo libsignal_jni.so "https://gitlab.com/packaging/libsignal-client/-/jobs/artifacts/v0.65.4/raw/libsignal-client/arm64/libsignal_jni.so?job=libsignal-client-arm64"
Then put the .so file in /usr/lib and run ldconfig:
sudo mv libsignal_jni.so /usr/lib/libsignal_jni.so
sudo ldconfig
Get a throwaway phone number. Or maybe you already have one. Or multiple! I don't judge.
Note
I do judge
It doesn't matter where you get it from, you only need it for the initial verification text.
For a free option, I just used voice.google.com to get a google number since its completely free, and I wasn't using it for anything else.
For the rest of this gist, I'l pretend my number is "(+1)234-567-8910", but obviously insert your own.
"32 (or 60 in some cases) is a widely accepted number due to its simplicity." - SignalBot, on its favorite phone number
Hurray! Now you should be able to run the Signal-CLI. Go to this link for a captcha:
https://signalcaptchas.org/registration/generate.html
Solve the captcha, proving you're not a robot once and for all. Then, right click the "Open Signal" link, and Copy Address.
Run your first command of the Signal-CLI, using that gigantic string of letters you just copied:
signal-cli -a +12345678910 register --captcha "signalcaptcha://signal-hcaptcha.5fad9...the really really long thing"
if all goes well, then you should receive a text with a confirmation code. Copy that confirmation code, and run
signal-cli -a +12345678910 verify 981448
Note
You may now cast your temporary number into the sea
If you want to verify that your account is working, try talking to yourself:
signal-cli -a +12345678910 send -m "I'm looking for a man in finance" +155533396887
Since you don't want to be a creepy bot, you should also go off and make a name for youself. How about "SignalBot"!
signal-cli -a +12345678910 updateProfile --name "SignalBot"
"What a great question! I'm an English teacher" - SignalBot, when asked what Smollm is
The next thing we need is the brains of the operation. The tiny, tiny brains.
First, install Ollama, a FOSS tool to run numerous LLMs:
curl -fsSL https://ollama.com/install.sh | sh
After waiting, you can install the tiniest model they have, Smollm
ollama pull smollm:135m
You can even start sampling it's amazing insights from right here in the command line:
ollama run smollm:135m "omg, are you the president of the united states??"
We'll need to set up a small python environment in our home directory to run a script on a loop.
mkdir signalbot
cd signalbot
python -m venv signalbotvenv
source signalbotvenv/bin/activate
pip install ollama
Now we need to set up the actual script! Below is the python script I'm using:
import subprocess
import time
import ollama
import multiprocessing
PHONE_NUMBER="+12345678910"
BOT_NAME="SignalBot"
LLM_TIMEOUT = 120
MODEL_NAME = 'smollm:135m'
def run_ollama_chat(prompt):
import ollama
reply = ollama.chat(
model=MODEL_NAME,
messages=[
{
'role': 'user',
'content': prompt
}
]
)
return reply['message']['content']
def call_llm_with_timeout(prompt, timeout=LLM_TIMEOUT):
# Run the LLM as a process so we can kill it if it gets stuck
pool = multiprocessing.Pool(processes=1)
async_result = pool.apply_async(run_ollama_chat, (prompt,))
try:
return async_result.get(timeout=timeout)
except multiprocessing.TimeoutError:
# Kill the worker process
pool.terminate()
return "Timeout error :("
finally:
pool.close()
pool.join()
def run_bot():
# Loop forever, receiving messages and responding to Mentions
command = ["signal-cli", "-u", PHONE_NUMBER, "receive"]
while True:
try:
# Receive Messages
result = subprocess.run(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
output = result.stdout.strip()
if output:
lines = output.split("\n")
for i in range(len(lines)):
# Check if bot was mentioned
if 'Mentions:' in lines[i] and BOT_NAME in lines[i+1]:
if 'Body: ' in lines[i-7]: # The body of the message should be 7 lines back
# Feed the message into the LLM
response = call_llm_with_timeout(lines[i-7])
# Group ID should be 5 lines above current line
group_id = lines[i-5].split()[-1]
# Send the response
send_command = [
"signal-cli",
"-u", PHONE_NUMBER,
"send",
"-m", f"{response}",
"-g", f"{group_id}"
]
_ = subprocess.run(
send_command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Wait a bit before checking again
time.sleep(1)
except KeyboardInterrupt:
print("\nScript stopped by user.")
break
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
run_bot()
So type
nano signalbot.py
And paste in the code above. You'll need to modify it depending on your phone number, the name of the bot (and the model name, if you decide to pick an actual useful model)
"I've been living on this planet for over 20 years, which means I have a intelligence that is far beyond what humans can achieve in their lifetime." - SignalBot, sounding like a recent college graduate
You can run the script right now if you want to:
python signalbot.py
But if it dies or your computer restarts, thats not ideal! We want the yuks to stay with us for eternity. Lets set this up as a linux service.
Run the following in your terminal:
sudo nano /etc/systemd/system/signalbot.service
and paste in the following.
[Unit]
Description=SignalBot Service
After=network-online.target
[Service]
Type=simple
User=signalbot
WorkingDirectory=/home/signalbot/signalbot
ExecStart=/home/harter/signalbot/signalbotvenv/bin/python /home/signalbot/signalbot/signalbot.py
Restart=always
[Install]
WantedBy=multi-user.target
Note that you'll need to modify the directories if you installed these scripts somewhere else.
Once that service file is in place, we just enable our service:
sudo systemctl daemon-reload
sudo systemctl enable signalbot.service
sudo systemctl start signalbot.service
Now SignalBot is just waiting patiently for messages to come in. How do we get it messages? Join a group!
Copy the invite URL from the group you want to join, and type:
signal-cli -u +12345678910 joinGroup --uri "[INVITE LINK]"
Note
If you don't have friends, Signal allows groups of 1
Now you're ready! Go out there and join the singularity!