top of page

TwitchPlays_TEMPLATE.py
for Python 3.x
[ published on 2021-12-16 ]

  1. # Written by DougDoug and DDarknut

  2.  

  3. # Hello! This file contains the main logic to process Twitch chat and convert it to game commands.

  4. # The code is written in Python 3.X

  5. # There are 2 other files needed to run this code:

  6.     # TwitchPlays_KeyCodes.py contains the key codes and functions to press keys in-game. You should not modify this file.

  7.     # TwitchPlays_Connection.py is the code that actually connects to Twitch. You should not modify this file.

  8.  

  9. # The source code primarily comes from:

  10.     # Wituz's "Twitch Plays" tutorial: http://www.wituz.com/make-your-own-twitch-plays-stream.html

  11.     # PythonProgramming's "Python Plays GTA V" tutorial: https://pythonprogramming.net/direct-input-game-python-plays-gta-v/

  12.     # DDarknut's message queue and updates to the Twitch networking code

  13.  

  14. # Disclaimer: 

  15.     # This code is NOT intended to be professionally optimized or organized.

  16.     # We created a simple version that works well for livestreaming, and I'm sharing it for educational purposes.

  17.  

  18. ##########################################################

  19.  

  20. TWITCH_CHANNEL = 'dougdougw' # Replace this with your Twitch username. Must be all lowercase.

  21.  

  22. ##########################################################

  23.  

  24. import keyboard

  25. import TwitchPlays_Connection

  26. import pydirectinput

  27. import random

  28. import pyautogui

  29. import concurrent.futures

  30. from TwitchPlays_KeyCodes import *

  31.  

  32. ##########################################################

  33.  

  34. # MESSAGE_RATE controls how fast we process incoming Twitch Chat messages. It's the number of seconds it will take to handle all messages in the queue.

  35. # This is used because Twitch delivers messages in "batches", rather than one at a time. So we process the messages over MESSAGE_RATE duration, rather than processing the entire batch at once.

  36. # A smaller number means we go through the message queue faster, but we will run out of messages faster and activity might "stagnate" while waiting for a new batch. 

  37. # A higher number means we go through the queue slower, and messages are more evenly spread out, but delay from the viewers' perspective is higher.

  38. # You can set this to 0 to disable the queue and handle all messages immediately. However, then the wait before another "batch" of messages is more noticeable.

  39. MESSAGE_RATE = 0.5

  40. # MAX_QUEUE_LENGTH limits the number of commands that will be processed in a given "batch" of messages. 

  41. # e.g. if you get a batch of 50 messages, you can choose to only process the first 10 of them and ignore the others.

  42. # This is helpful for games where too many inputs at once can actually hinder the gameplay.

  43. # Setting to ~50 is good for total chaos, ~5-10 is good for 2D platformers

  44. MAX_QUEUE_LENGTH = 20  

  45. MAX_WORKERS = 100 # Maximum number of threads you can process at a time 

  46.  

  47. last_time = time.time()

  48. message_queue = []

  49. thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS)

  50. active_tasks = []

  51. pyautogui.FAILSAFE = False

  52.  

  53. ##########################################################

  54.  

  55. # An optional count down before starting, so you have time to load up the game

  56. countdown = 10

  57. while countdown > 0:

  58.     print(countdown)

  59.     countdown -= 1

  60.     time.sleep(1)

  61. ​

  62. t = TwitchPlays_Connection.Twitch();

  63. t.twitch_connect(TWITCH_CHANNEL);

  64. ​

  65. def handle_message(message):

  66.     try:

  67.         msg = message['message'].lower()

  68.         username = message['username'].lower()

  69.  

  70.         print("Got the message: [" + msg + "] from user [" + username + "]")

  71.  

  72.         # Now that you have a chat message, this is where you add your game logic.

  73.         # Use the "HoldKey(KEYCODE)" function to press and hold down a keyboard key.

  74.         # Use the "ReleaseKey(KEYCODE)" function to release a specific keyboard key.

  75.         # Use the "HoldAndReleaseKey(KEYCODE, SECONDS)" function press down a key for X seconds, then release it.

  76.         # Use the pydirectinput library to press or move the mouse

  77.  

  78.         # I've added some example videogame logic code below:

  79.  

  80.         ###################################

  81.         # Example GTA V Code 

  82.         ###################################

  83.  

  84.         # If the chat message is "left", then hold down the A key for 2 seconds

  85.         if msg == "left"

  86.             HoldAndReleaseKey(A, 2)

  87.  

  88.         # If the chat message is "right", then hold down the D key for 2 seconds

  89.         if msg == "right"

  90.             HoldAndReleaseKey(D, 2)

  91.  

  92.         # If message is "drive", then permanently hold down the W key

  93.         if msg == "drive"

  94.             ReleaseKey(S) #release brake key first

  95.             HoldKey(W) #start permanently driving

  96.  

  97.         # If message is "reverse", then permanently hold down the S key

  98.         if msg == "reverse"

  99.             ReleaseKey(W) #release drive key first

  100.             HoldKey(S) #start permanently reversing

  101.  

  102.         # Release both the "drive" and "reverse" keys

  103.         if msg == "stop"

  104.             ReleaseKey(W)

  105.             ReleaseKey(S)

  106.  

  107.         # Press the spacebar for 0.7 seconds

  108.         if msg == "brake"

  109.             HoldAndReleaseKey(SPACE, 0.7)

  110.  

  111.         # Press the left mouse button down for 1 second, then release it

  112.         if msg == "shoot"

  113.             pydirectinput.mouseDown(button="left")

  114.             time.sleep(1)

  115.             pydirectinput.mouseUp(button="left")

  116.  

  117.         ####################################

  118.         ####################################

  119.  

  120.     except Exception as e:

  121.         print("Encountered exception: " + str(e))

  122.  

  123. ​

  124. while True:

  125.  

  126.     active_tasks = [t for t in active_tasks if not t.done()]

  127.  

  128.     #Check for new messages

  129.     new_messages = t.twitch_receive_messages();

  130.     if new_messages:

  131.         message_queue += new_messages; # New messages are added to the back of the queue

  132.         message_queue = message_queue[-MAX_QUEUE_LENGTH:] # Shorten the queue to only the most recent X messages

  133.  

  134.     messages_to_handle = []

  135.     if not message_queue:

  136.         # No messages in the queue

  137.         last_time = time.time()

  138.     else:

  139.         # Determine how many messages we should handle now

  140.         r = 1 if MESSAGE_RATE == 0 else (time.time() - last_time) / MESSAGE_RATE

  141.         n = int(r * len(message_queue))

  142.         if n > 0:

  143.             # Pop the messages we want off the front of the queue

  144.             messages_to_handle = message_queue[0:n]

  145.             del message_queue[0:n]

  146.             last_time = time.time();

  147.  

  148.     # If user presses Shift+Backspace, automatically end the program

  149.     if keyboard.is_pressed('shift+backspace'):

  150.         exit()

  151.  

  152.     if not messages_to_handle:

  153.         continue

  154.     else:

  155.         for message in messages_to_handle:

  156.             if len(active_tasks) <= MAX_WORKERS:

  157.                 active_tasks.append(thread_pool.submit(handle_message, message))

  158.             else:

  159.                 print(f'WARNING: active tasks ({len(active_tasks)}) exceeds number of workers ({MAX_WORKERS}). ({len(message_queue)} messages in the queue)')

End of Document

bottom of page