This project combines a few things that made it interesting to me. First, there’s the I Ching. I read from mine every day, and it’s not so long, which makes it a good source of data to use in a case like this. Secondly, there’s some API usage and networked programming, which is something I’m working to get better at. And lastly, it’s something fun for Slack. My college uses slack as the main comms tool for the CS department, and most of my friends and I chat there every day.
What does the program do? Nothing wild. You type /tao in slack, and you get a line from the I Ching returned in chat. As with all things, it sounds simple. That’s sort of the point, I Ching and all.
Here’s a list of what we’ve got to do to make this happen, then we’ll go through it. I’ll describe the process breadth-first, not depth. There’s lots of in-depth guides to each of these steps out there online already, so i hope you can find extra help for anything that’s slowing you down too much.
- Create a bot for Slack.
- Get the Data
- Write the bot program
- Configure Raspberry Pi
- Put the parts together
Creating a Bot for Slack
First, we go to https://api.slack.com/apps/ to create an app. Make a note of the OAuth and Bot OAuth tokens, we’ll need those later.
You’ll also want to create a slash command, so your bot does something. I used /tao, simple enough. Set the request URL to be your router IP and a port of your choosing, which we’ll get to later. This is how people will “call” your bot, and where your bot will “hear” that it’s been called.
Getting the Data
The source I’m using is Ron Hogan’s Tao.txt. It’s available here: http://www.beatrice.com/TAO.txt. It’s a nice version, and conveniently all in a simple .txt version already. I copy most of it into a text file, and write a short python script to cut it into a list of parts that roughly go together.
with open ("tao.txt") as f: tao = f.read().split("\n") posts = [''] * 663 pointer = 0 for r in range(1, len(tao)-1): if len(tao[r]) > 4: posts[pointer] += " " + tao[r] if "." in tao[r]: pointer += 1
This will roughly arrange our data into a line separated list of individual “taos”, ending each one with a period. It also disregards the lines which are exclusively line breaks or numbers. This solution isn’t perfect, since some of these lines come in pairs, but it works about 95% of the time.
Write the Bot Program
First, some setup. This program I’ll call taobot.py. Note that I’m not writing this program on the pi- it’s just from my laptop, which is simply for ease of use.
import os import slack import socket import threading import random #My improvised way of not committing my API keys with open ("../secrets.txt") as f: secrets = f.read().split("\n") slack_token = secrets bot_user_token = secrets bind_ip = "192.168.1.243" bind_port = 8100 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((bind_ip, bind_port)) server.listen(5) print("[*] Ready on %s:%d" % (bind_ip, bind_port))
A few things to note here:
- We need to import some slack packages, which we’ll deal with later.
- We need a way of getting those API keys from earlier. In this case, I created a text file with the two strings in them, then read them like above. This means I cam commit my directory to Github, but the code will reach out of the commit scope to get the secrets. It works.
- We need an IP and a port. The IP in this case is the local IP of the Pi on the network. (Don’t worry if you don’t know this yet, it’s easy to change later). The port can really be almost anything not already in use, but we’ll need to remember it later.
- Some socket mumbo jumbo.
Next, we’ll add a few more functions that we’ll be needing for various tasks.
def get_tao(): with open("formatted_tao.txt") as g: taos = g.read().splitlines() quote = (taos[random.randint(0, 663)]) quote += "\n _-from Ron Hogan's TAO.txt_" return quote #I'm so sure there's a better way to do this def get_term(raw, term): raw = raw.decode("utf-8") raw = raw.split("&") for r in raw: if term in r: r=r.split("=") print(r) return(r) def make_post(channel): client = slack.WebClient(slack_token) response = client.chat_postMessage( channel=channel, text=get_tao())
- get_tao() will randomly choose one of our line-seperated taos, which we generated above. I’ve copied and pasted all of those into a file called formatted_tao.txt.
- get_terms() is my way of checking what channel taobot was called for, which will make more sense once we get to the networking section in the next code block.
- make_post() sends the results of a get_tao() off to slack, using our tokens and the Slack API.
Last bit, the main loop:
#client-handling thread def handle_client(client_socket): raw = client_socket.recv(1024) channel = get_term(raw, "channel_name") make_post(channel) print("Sent TAO to channel:" + channel) #I have no idea why this behaves the way it does, but it solves my 503 #timeout problem. This shows the response message privately to whoever calls #/tao, and also acts as a HTTP 200 response to the slack API. response = "Thanks for using /tao.".encode() client_socket.send(response) client_socket.close() while True: client,addr = server.accept() client_handler = threading.Thread(target=handle_client,args=(client,)) client_handler.start()
This just while(true)’s the running of the bot. It listens for a /tao to be called, then will send off the data.
This brings me to a current not-so-smoothly done part of the project. For a long time, the bot would shows a 503 timeout response to the user calling the bot, despite ALSO working correctly. I couldn’t figure out what was up with it, but in testing some things out, I discovered this: if I send an encoded string back, like the “Thanks for using /tao” message, it appears to the sender as a “visible to only you” message, and resolves the 503 timeout problem. It’s not great, but it works- It’s the best I’ve got so far, and I’ll update if I can figure something better out.
Configuring the Pi
I’ll move through this quickly, since there’s a thousand guides on this. You’ll need to make a bootable micro-SD card, probably running raspbian, and turn the Pi on. Once it’s on, I pretty much follow this set of steps to get everything set up. Plug the Pi’s ethernet into your router, and get its local IP. You’ll need to then forward the ports on your router to the Pi, and make sure the line in the taobot.py file has the right IP in it.
The part of this whole program that slowed me down the most was getting the slack and slackclient packages running properly on the Pi. The thing that saved me was learning that pip can be run as pip, pip3, or finally, pip3.6! The Slack package needs to be run with python 3.6, but installing it with “pip3 install slack” will install it for python 3.4. pip3.6 lets you install packages specifically for python 3.6, even with other version of python on the same Pi.
Put it all together
Everything should be all set. Make sure everything can reach everything else:
- The Taobot.py is on the Pi and running
- The Pi is running the proper versions of Python (3.6) and of the Slack packages (at least 2.0.0, IIRC)
- Your ports are forwarded on your router, set in the Taobot.py, and in your slash command for your slack bot configuration
- You’ve got your API keys accessible to Taobot.py (But not anyone else)
With all that, you should be running! I enjoyed making this program, and hope that my guide will be useful to anyone setting out to do something similar. If you’ve got troubleshooting questions, you’ve already made it to my site, so reaching out to me isn’t too hard to find. I’d be happy to personally guide you through your project.