Writing SchmeckleBot, a simple Reddit bot

This post will discuss the ideation and building of SchmeckleBot. So, before I wrote the Tensorflow chessbot, I wanted to learn the basics of building Reddit bots, and decided to choose something that was both fun and solved an existing problem.

I frequent the /r/rickandmorty subreddit, which if you haven't heard about it is a place to talk about Rick and Morty, a hilarious and awesome TV show (check it out if you're into animated TV shows like Futurama or Archer). Every so often redditors comment on a post saying they'll buy something for X amount of schmeckles, a unit of currency from one of the episodes of Rick and Morty. Here's one such post


Boogilywoo2 411 points  
Hey there! I'm Mr.SculptureBuyer. I'll pay 50 schmeckles for that sculpture!


According to the creator Dan Harmond a schmeckle is about $148 (the reason for this conversion is pretty great). So 50 schmeckles would be $7,400 USD. We want a bot that responds something like this (in fact, exactly like this, since this is the final result).


Boogilywoo2 411 points  
Hey there! I'm Mr.SculptureBuyer. I'll pay 50 schmeckles for that sculpture!
SchmeckleBot 281 points  
Hey there! I'm Mr.SculptureBuyer. I'll pay 50 schmeckles for that sculpture!
  • 50 Schmeckles → $7,400 USD

1 Schmeckle = $148 USD | price not guaranteed | what is my purpose


So, let's make a bot that listens on the r/rickandmorty subreddit for comments or posts which have X schmeckles, and respond with the conversion to USD. This is a simple and fun way to learn to use the python API wrapper for reddit, PRAW. First we created a new user account called SchmeckleBot on reddit, and now we need to login using its credentials.

# Login
r = praw.Reddit(user_agent=user_agent)

# Login with account username/password
r.login(auth_config.USERNAME, auth_config.PASSWORD, disable_warning=True)

# Get accessor to comments
subreddit = r.get_subreddit('rickandmorty')

Logging in is quite simple, our user agent is a short string to describe what the bot will do, and we store our account username/password in a separate auth_config.py to keep it secret by excluding it in our .gitignore file.

Then, we can use regex to search within comments/post titles for 'X schmeckles' where X is a number.


# Look for '<number> schmeckle' ignore case (schmeckles accepted implicitly)
# Works for positive negative floats, but fails softly on EXP
p = re.compile('(-?[\d|,]*\.{0,1}\d+ schmeckle[\w]*)', re.IGNORECASE)

This allows us to get variations like 10.34 schmeckles or even 1,234 schmeckleronis. I added a few other things such as ignoring quoted sentences due to waking up one day to the laptop humming away furiously in a bot-battle with another bot that replies to the previous comment in quotes (making a little cameo in /r/botrights).

The actual conversion is dead simple, to make things a little more interesting generating the response text will include the quoted message being responded to, with the X schmeckles part bolded, and it will do it for every instance found within the comment.

def schmeckle2usd(schmeckle):
  """1 Schmeckle = $148 USD
  https://www.reddit.com/r/IAmA/comments/202owt/we_are_dan_harmon_and_justin_roiland_creators_of/cfzfv79"""
  return schmeckle * 148.0

def getConversion(body):
  """Calculate schmeckle to USD responses"""
  values = []
  msg_template_f = u"* {:,.2f} Schmeckles → **${:,.2f} USD**\n" # with decimals
  msg_template_i = u"* {:,.0f} Schmeckles → **${:,.0f} USD**\n" # without decimals
  msg_inf = u"* There's a lot of numbers there, I think you could probably go to Wall Street.\n\n*You ever hear about Wall Street, Morty? Y-Y-Y'know what those guys do i-in-in their fancy boardrooms? They take their balls and they dip 'em in cocaine and wipe 'em all over each other—y'know.*\n"
  pairs = p.findall(body)
  if len(pairs) > 0:
    for match in pairs:
      # '<number> schmeckle' -> match, float(<number>)
      value_str = match.split()[0]

      # Handle numbers with over 9000 characters. Yes, it's over 9000.
      if (len(value_str)) > 9000:
        values.append(locale.atof('inf'))
      else:
        values.append(locale.atof(value_str))
  
  response = []
  for schmeckle in values:
    if isinf(schmeckle):
      response.append(msg_inf)
    else:
      usd = schmeckle2usd(schmeckle)
      if schmeckle.is_integer():
        response.append(msg_template_i.format(schmeckle, usd))
      else:
        response.append(msg_template_f.format(schmeckle, usd))
  
  return [response, values]

This is pretty much the meat of the thing, the rest is interfacing and listening on the sub-reddit. We set this up running on an old laptop that was requisitioned as a server, in fact the same one as our tensorflow chessbot. A nice little weekend project that has received positive feedback from the community, this and ExistentialRickBots are two extremely simple Reddit bots that provide a simple way to interact with the a large amount of users as an autonomous agent, with more agency than a simple chatbot, and a longer history than IRC channels, where the conversations get lost in time.

Since this was my first Reddit bot, the comment and post handling is pretty rough, we start to clean up our act with our next bot, ExistentialRickBot.

Have a look at its most recent responses on it's user page on its SchmeckleBot Reddit user page.
And feel free to check out the full source code here: Source code on Github

Popular posts from this blog

Building a PID hover controller for Kerbal Space Program with kOS and IPython Notebook

Learning TensorFlow #1 - Using Computer Vision to turn a Chessboard image into chess tiles

Learning TensorFlow #2 - Predicting chess pieces from images using a single-layer classifier