TwitchPlays.py

for Python 2.7

[ published on 2019-10-31 ]

  1. # Written by DougDoug (DougDoug on Youtube, DougDougW on Twitch)

  2. # Hello!

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

  4. # All sections that you need to update are labeled with a "TODO" comment.

  5. # The source code primarily comes from:

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

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

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

  9.     # TwitchPlays_AccountInfo.py is where you put your Twitch username and OAuth token. This is to keep your account details separated from the main source code.

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

  11. # Disclaimer: 

  12.     # This code is NOT optimized or well-organized. I am not a Python programmer.

  13.     # I created a simple version that works quickly, and I'm sharing it for educational purposes.

  14. ###############################################

  15. # Import and define our functions / key codes to send key commands

  16. # General imports

  17. import time

  18. import subprocess

  19. import ctypes

  20. import random

  21. import string

  22. # Twitch imports

  23. import TwitchPlays_Connection

  24. from TwitchPlays_AccountInfo import TWITCH_USERNAME, TWITCH_OAUTH_TOKEN

  25. # Controller imports

  26. import pyautogui

  27. import pynput

  28. from pynput.mouse import Button, Controller

  29. SendInput = ctypes.windll.user32.SendInput

  30. # KEY PRESS NOTES

  31. # The standard "Twitch Plays" tutorial key commands do NOT work in DirectX games (they only work in general windows applications)

  32. # Instead, we use DirectX key codes and input functions below.

  33. # This DirectX code is partially sourced from: https://stackoverflow.com/questions/53643273/how-to-keep-pynput-and-ctypes-from-clashing

  34. # Presses and permanently holds down a keyboard key

  35. def PressKeyPynput(hexKeyCode):

  36.     extra = ctypes.c_ulong(0)

  37.     ii_ = pynput._util.win32.INPUT_union()

  38.     ii_.ki = pynput._util.win32.KEYBDINPUT(0, hexKeyCode, 0x0008, 0, ctypes.cast(ctypes.pointer(extra), ctypes.c_void_p))

  39.     x = pynput._util.win32.INPUT(ctypes.c_ulong(1), ii_)

  40.     SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

  41. # Releases a keyboard key if it is currently pressed down

  42. def ReleaseKeyPynput(hexKeyCode):

  43.     extra = ctypes.c_ulong(0)

  44.     ii_ = pynput._util.win32.INPUT_union()

  45.     ii_.ki = pynput._util.win32.KEYBDINPUT(0, hexKeyCode, 0x0008 | 0x0002, 0, ctypes.cast(ctypes.pointer(extra), ctypes.c_void_p))

  46.     x = pynput._util.win32.INPUT(ctypes.c_ulong(1), ii_)

  47.     SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

  48. # Helper function. Holds down a key for the specified number of seconds, then releases it.

  49. def PressAndHoldKey(hexKeyCode, seconds):

  50.     PressKeyPynput(hexKeyCode)

  51.     time.sleep(seconds)

  52.     ReleaseKeyPynput(hexKeyCode)

  53. # Mouse Controller, using pynput

  54.     # pynput.mouse functions are found at: https://pypi.org/project/pynput/

  55.     # NOTE: pyautogui's click() function permanently holds down in DirectX, so I used pynput instead for mouse instead.

  56. mouse = Controller()

  57. ###############################################

  58. # DIRECTX KEY CODES

  59. # These codes identify each key on the keyboard.

  60. # Note that DirectX's key codes (or "scan codes") are NOT the same as Windows virtual hex key codes. 

  61. #   DirectX codes are found at: https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa299374(v=vs.60)

  62. Q = 0x10

  63. W = 0x11

  64. E = 0x12

  65. R = 0x13

  66. T = 0x14

  67. Y = 0x15

  68. U = 0x16

  69. I = 0x17

  70. O = 0x18

  71. P = 0x19

  72. A = 0x1E

  73. S = 0x1F

  74. D = 0x20

  75. F = 0x21

  76. G = 0x22

  77. H = 0x23

  78. J = 0x24

  79. K = 0x25

  80. L = 0x26

  81. Z = 0x2C

  82. X = 0x2D

  83. C = 0x2E

  84. V = 0x2F

  85. B = 0x30

  86. N = 0x31

  87. M = 0x32

  88. ESC = 0x01

  89. ONE = 0x02

  90. TWO = 0x03

  91. THREE = 0x04

  92. FOUR = 0x05

  93. FIVE = 0x06

  94. SIX = 0x07

  95. SEVEN = 0x08

  96. EIGHT = 0x09

  97. NINE = 0x0A

  98. ZERO = 0x0B

  99. MINUS = 0x0C

  100. EQUALS = 0x0D

  101. BACKSPACE = 0x0E

  102. SEMICOLON = 0x27

  103. TAB = 0x0F

  104. CAPS = 0x3A

  105. ENTER = 0x1C

  106. LEFT_CONTROL = 0x1D

  107. LEFT_ALT = 0x38

  108. LEFT_SHIFT = 0x2A

  109. SPACE = 0x39

  110. DELETE = 0x53

  111. COMMA = 0x33

  112. PERIOD = 0x34

  113. BACKSLASH = 0x35

  114. NUMPAD_0 = 0x52

  115. NUMPAD_1 = 0x4F

  116. NUMPAD_2 = 0x50

  117. NUMPAD_3 = 0x51

  118. NUMPAD_4 = 0x4B

  119. NUMPAD_5 = 0x4C

  120. NUMPAD_6 = 0x4D

  121. NUMPAD_7 = 0x47

  122. NUMPAD_8 = 0x48

  123. NUMPAD_9 = 0x49

  124. NUMPAD_PLUS = 0x4E

  125. NUMPAD_MINUS = 0x4A

  126. LEFT_ARROW = 0xCB

  127. RIGHT_ARROW = 0xCD

  128. UP_ARROW = 0xC8

  129. DOWN_ARROW = 0xD0

  130. LEFT_MOUSE = 0x100

  131. RIGHT_MOUSE = 0x101

  132. MIDDLE_MOUSE = 0x102

  133. MOUSE3 = 0x103

  134. MOUSE4 = 0x104

  135. MOUSE5 = 0x105

  136. MOUSE6 = 0x106

  137. MOUSE7 = 0x107

  138. MOUSE_WHEEL_UP = 0x108

  139. MOUSE_WHEEL_DOWN = 0x109

  140. ########################################################

  141. # An optional countdown before the code actually starts running, so you have time to load up the game before messages are processed.

  142. # TODO: Set the "countdown" variable to whatever countdown length you want.

  143. countdown = 5 #The number of seconds before the code starts running

  144. while countdown > 0:

  145.     print(countdown)

  146.     countdown -= 1

  147.     time.sleep(1)

  148. # Connects to your twitch chat, using your username and OAuth token.

  149. # TODO: make sure that your Twitch username and OAuth token are added to the "TwitchPlays_AccountInfo.py" file

  150. t = TwitchPlays_Connection.Twitch();

  151. t.twitch_connect(TWITCH_USERNAME, TWITCH_OAUTH_TOKEN);

  152. ##########################################################

  153. while True:

  154.     # Check for new chat messages

  155.     new_messages = t.twitch_recieve_messages();

  156.     if not new_messages:

  157.         #No new messages. 

  158.         continue

  159.     else:

  160.         try:  

  161.             for message in new_messages:

  162.                 # We got a new message! Get the message and the username.

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

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

  165.                 

  166.                 # TODO:

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

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

  169.                 # Use the "ReleaseKeyPynput(KEYCODE)" function to release a specific keyboard key.

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

  171.                 # Use "mouse.press(Button.left)" or "mouse.release(Button.left)" to press/release the mouse. Can use Button.right for right click.

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

  173.                 ###################################

  174.                 # Example GTA V Code 

  175.                 ###################################

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

  177.                 if msg == "left"

  178.                     PressAndHoldKey(A, 2)

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

  180.                 if msg == "right"

  181.                     PressAndHoldKey(D, 2)

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

  183.                 if msg == "drive"

  184.                     ReleaseKeyPynput(S) #release brake key first

  185.                     PressKeyPynput(W) #start permanently driving

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

  187.                 if msg == "reverse"

  188.                     ReleaseKeyPynput(W) #release drive key first

  189.                     PressKeyPynput(S) #start permanently reversing

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

  191.                 if msg == "stop"

  192.                     ReleaseKeyPynput(W)

  193.                     ReleaseKeyPynput(S)

  194.                 # Press the spacebar for 0.7 seconds

  195.                 if msg == "brake"

  196.                     PressAndHoldKey(SPACE, 0.7)

  197.                 # Presses the left mouse button down for 1 second, then releases it

  198.                 if msg == "shoot"

  199.                     mouse.press(Button.left)

  200.                     time.sleep(1)

  201.                     mouse.release(Button.left)

  202.                 ###################################

  203.                 # Example Miscellaneous Code 

  204.                 ###################################

  205.                 

  206.                 # Clicks and drags the mouse upwards, using the Pyautogui commands.

  207.                 # NOTE: unfortunately, Pyautogui does not work in DirectX games like GTA V. It will work in all other environments (e.g. on your desktop)

  208.                 # If anyone finds a reliable way to move the mouse in DirectX games, please let me know!

  209.                 if msg == "drag mouse up":

  210.                     pyautogui.drag(0, -50, 0.25, button='left')

  211.                 # Clicks and drags the mouse downwards, using the Pyautogui commands

  212.                 if msg == "drag mouse down":

  213.                     pyautogui.drag(0, 50, 0.25, button='left')

  214.                 # An example of pressing 2 keys at once.

  215.                 # First holds down the LEFT_CONTROL key, then presses the A key for 0.1 seconds, then releases the LEFT_CONTROL key.

  216.                 if msg == "select all":

  217.                     PressKeyPynput(LEFT_CONTROL)

  218.                     PressAndHoldKey(A, 0.1)

  219.                     ReleaseKeyPynput(LEFT_CONTROL)

  220.                 

  221.                 # Can use pyautogui.typewrite() to type messages from chat into the keyboard.

  222.                 # Here, if a chat message says "type ...", it will type out their text.

  223.                 if msg.startswith("type "): 

  224.                     try:

  225.                         typeMsg = msg[5:] # Ignore the "type " portion of the message

  226.                         pyautogui.typewrite(typeMsg)

  227.                     except:

  228.                         # There was some issue typing the msg. Print it out, and move on.

  229.                         print("Typing this particular message didn't work: " + msg)

  230.                 ####################################

  231.                 ####################################

  232.         except:

  233.             # There was some error trying to process this chat message. Simply move on to the next message.

  234.             print('Encountered an exception while reading chat.')

End of Document