Skip to content

Instantly share code, notes, and snippets.

@mjdarby
Last active February 1, 2020 23:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mjdarby/19683c05870d5426c5413bf6aeaba73f to your computer and use it in GitHub Desktop.
Save mjdarby/19683c05870d5426c5413bf6aeaba73f to your computer and use it in GitHub Desktop.
Mario Maker Level Code Twitch Bot

A cobbled-together Twitch bot that uses Tesseract to do OCR on your current window to try and find Mario Maker level codes, and then serves the last seen level code on request.

It's a bit naff, but as a PoC for a Python-based Twitch bot it's not awful. Rips some example code from the Python IRC project and gets it all Twitch'y.

To setup:

  1. Install Python pre-reqs as per requirements.txt
  2. Install Tesseract and make sure tesseract.exe is in your PATH
  3. Get a Twitch OAuth code. The Twitch recommended way of doing this comes from their Chatbot guide: use the TwitchApps Oauth token creator
  4. In main.py, replace your_twitch_name with your name (or your chatbot's name, if you're going to use a separate account for it)
  5. In main.py, replace your_oauth_key with the key generated in step 3.

To use:

  1. python mariobot.py
  2. Quit the bot by saying !disconnect in the chat while logged in to that account - or modify the snippet to allow any arbitrary user to cause a disconnect.
  3. If the bot spots a string like xxx-xxx-xxx it will store it and spit it back out whenever a user types !level in the chat.

Improvements:

  1. Allow user to specify where on the screen they want to monitor for level codes
  2. ???
  3. PROFIT!!!
#!/usr/bin/env python
import signal
import sys
import ssl
import argparse
import re
import threading
import irc.client
import jaraco.logging
import pytesseract
from PIL import Image
from PIL import ImageGrab
# Nuclear button because the thread logic is a bit naff
def signal_handler(sig, frame):
print('Bye!')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
current_level_code = None
kill = False
# Periodically
def scanForLevelCode():
global current_level_code
# Capture the screen and run it through the OCR
image_string = pytesseract.image_to_string(ImageGrab.grab())
# Try and extract a level code from it
m = re.search('(\w\w\w-\w\w\w-\w\w\w)', image_string)
if m:
current_level_code = m.group(0)
print("New level code found")
print(m.group(0))
if not kill:
threading.Timer(1, scanForLevelCode).start()
scanForLevelCode()
def on_connect(connection, event):
if irc.client.is_channel(channel):
connection.join(channel)
return
main_loop(connection)
def on_join(connection, event):
main_loop(connection)
def main_loop(connection):
connection.privmsg(channel, "Mario Helper, ready and waiting!")
def on_pubmsg(connection, event):
sender = event.source.split('!')[0]
text = event.arguments[0].strip()
# Returns latest seen level
if (text == "!level"):
if (current_level_code is None):
connection.privmsg(channel, "No level available.")
else:
connection.privmsg(channel, "Mario Helper: " + current_level_code)
# Disconnects the bot, which will in turn end the program
# Only accepted if user requesting it is the bot user itself.
if (text == "!disconnect" and sender == nickname):
connection.privmsg(channel, "Mario Helper disconnecting, see you later!")
connection.quit()
def on_disconnect(connection, event):
# Stop the image processing thread from running.
global kill
kill = True
# Seeya!
raise SystemExit()
server = "irc.chat.twitch.tv"
port = 6697
nickname = "your_twitch_name"
channel = "#your_twitch_name"
oauth = "your_oauth_key"
def main():
parser = argparse.ArgumentParser()
jaraco.logging.add_arguments(parser)
jaraco.logging.setup(parser.parse_args())
ssl_factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
reactor = irc.client.Reactor()
try:
c = reactor.server().connect(server, port, nickname, "oauth:"+oauth, connect_factory=ssl_factory)
except irc.client.ServerConnectionError:
print(sys.exc_info()[1])
raise SystemExit(1)
c.add_global_handler("welcome", on_connect)
c.add_global_handler("join", on_join)
c.add_global_handler("disconnect", on_disconnect)
c.add_global_handler("pubmsg", on_pubmsg)
reactor.process_forever()
if __name__ == "__main__":
main()
importlib-metadata==1.5.0
importlib-resources==1.0.2
irc==18.0.0
jaraco.classes==3.1.0
jaraco.collections==3.0.0
jaraco.functools==3.0.0
jaraco.logging==3.0.0
jaraco.stream==3.0.0
jaraco.text==3.2.0
more-itertools==8.2.0
Pillow==7.0.0
pytesseract==0.3.2
pytz==2019.3
six==1.14.0
tempora==2.1.0
zipp==2.1.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment