TwitchPlays_TEMPLATE.py
for Python 3.9.x
[ published on 2022-08-16 ]

  1. import concurrent.futures

  2. import random

  3. import keyboard

  4. import pydirectinput

  5. import pyautogui

  6. import TwitchPlays_Connection

  7. from TwitchPlays_KeyCodes import *

  8. ##################### GAME VARIABLES #####################

  9. # Replace this with your Twitch username. Must be all lowercase.

  10. TWITCH_CHANNEL = 'dougdougw' 

  11. # If streaming on Youtube, set this to False

  12. STREAMING_ON_TWITCH = True

  13.  

  14. # Replace this with your Youtube's Channel ID

  15. # Find this by clicking your Youtube profile pic -> Settings -> Advanced Settings

  16. YOUTUBE_CHANNEL_ID = "YOUTUBE_CHANNEL_ID_HERE" 

  17. # If you're using an Unlisted stream to test on Youtube, replace "None" below with your stream's URL in quotes.

  18. # Otherwise you can leave this as "None"

  19. YOUTUBE_STREAM_URL = None

  20. ##################### MESSAGE QUEUE VARIABLES #####################

  21. # 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.

  22. # 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.

  23. # 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. 

  24. # 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.

  25. # 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.

  26. MESSAGE_RATE = 0.5

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

  28. # 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.

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

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

  31. MAX_QUEUE_LENGTH = 20

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

  33.  

  34. last_time = time.time()

  35. message_queue = []

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

  37. active_tasks = []

  38. pyautogui.FAILSAFE = False

  39. ##########################################################

  40. # Count down before starting, so you have time to load up the game

  41. countdown = 0

  42. while countdown > 0:

  43.     print(countdown)

  44.     countdown -= 1

  45.     time.sleep(1)

  46. if STREAMING_ON_TWITCH:

  47.     t = TwitchPlays_Connection.Twitch()

  48.     t.twitch_connect(TWITCH_CHANNEL)

  49. else:

  50.     t = TwitchPlays_Connection.YouTube()

  51.     t.youtube_connect(YOUTUBE_CHANNEL_ID, YOUTUBE_STREAM_URL)

  52. def handle_message(message):

  53.     try:

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

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

  56.         print("Got this message from " + username + ": " + msg)

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

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

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

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

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

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

  63.         ###################################

  64.         # Example GTA V Code 

  65.         ###################################

  66.  

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

  68.         if msg == "left": 

  69.             HoldAndReleaseKey(A, 2)

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

  71.         if msg == "right": 

  72.             HoldAndReleaseKey(D, 2)

  73.  

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

  75.         if msg == "drive": 

  76.             ReleaseKey(S) #release brake key first

  77.             HoldKey(W) #start permanently driving

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

  79.         if msg == "reverse": 

  80.             ReleaseKey(W) #release drive key first

  81.             HoldKey(S) #start permanently reversing

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

  83.         if msg == "stop": 

  84.             ReleaseKey(W)

  85.             ReleaseKey(S)

  86.         # Press the spacebar for 0.7 seconds

  87.         if msg == "brake": 

  88.             HoldAndReleaseKey(SPACE, 0.7)

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

  90.         if msg == "shoot": 

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

  92.             time.sleep(1)

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

  94.         # Move the mouse up by 30 pixels

  95.         if msg == "aim up":

  96.             pydirectinput.moveRel(0, -30, relative=True)

  97.         # Move the mouse right by 200 pixels

  98.         if msg == "aim right":

  99.             pydirectinput.moveRel(200, 0, relative=True)

  100.         ####################################

  101.         ####################################

  102.     except Exception as e:

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

  104. while True:

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

  106.     #Check for new messages

  107.     new_messages = t.twitch_receive_messages();

  108.     if new_messages:

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

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

  111.  

  112.     messages_to_handle = []

  113.     if not message_queue:

  114.         # No messages in the queue

  115.         last_time = time.time()

  116.     else:

  117.         # Determine how many messages we should handle now

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

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

  120.         if n > 0:

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

  122.             messages_to_handle = message_queue[0:n]

  123.             del message_queue[0:n]

  124.             last_time = time.time();

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

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

  127.         exit()

  128.     if not messages_to_handle:

  129.         continue

  130.     else:

  131.         for message in messages_to_handle:

  132.             if len(active_tasks) <= MAX_WORKERS:

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

  134.             else:

  135.                 print(f'WARNING: active tasks ({len(active_tasks)}) exceeds number of workers

  136. ({MAX_WORKERS}). ({len(message_queue)} messages in the queue)')

End of Document