TwitchPlays_Connection.py
for Python 3.x
[ published on 2021-12-16 ]
-
# DougDoug Note:
-
# This is the code that connects to Twitch and checks for new messages.
-
# You should not need to modify anything in this file, just use as is.
-
# Original code by Wituz, updated by DDarknut
-
-
import sys
-
import socket
-
import re
-
import random
-
import time
-
-
MAX_TIME_TO_WAIT_FOR_LOGIN = 3
-
-
class Twitch:
-
re_prog = None
-
sock = None
-
partial = b''
-
login_ok = False
-
channel = ''
-
login_timestamp = 0
-
-
def twitch_connect(self, channel):
-
if self.sock: self.sock.close()
-
self.sock = None
-
self.partial = b''
-
self.login_ok = False
-
self.channel = channel
-
-
# Compile regular expression
-
self.re_prog = re.compile(b'^(?::(?:([^ !\r\n]+)![^ \r\n]*|[^ \r\n]*) )?([^ \r\n]+)(?: ([^:\r\n]*))?(?: :([^\r\n]*))?\r\n', re.MULTILINE)
-
-
# Create socket
-
print('Connecting to Twitch...')
-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
-
# Attempt to connect socket
-
self.sock.connect(('irc.chat.twitch.tv', 6667))
-
-
# Log in anonymously
-
user = 'justinfan%i' % random.randint(10000, 99999)
-
print('Connected to Twitch. Logging in anonymously...')
-
self.sock.send(('PASS asdf\r\nNICK %s\r\n' % user).encode())
-
-
self.sock.settimeout(1.0/60.0)
-
-
self.login_timestamp = time.time()
-
-
# Attempt to reconnect after a delay
-
def reconnect(self, delay):
-
time.sleep(delay)
-
self.twitch_connect(self.channel)
-
-
# Returns a list of irc messages received
-
def receive_and_parse_data(self):
-
buffer = b''
-
while True:
-
received = b''
-
try:
-
received = self.sock.recv(4096)
-
except socket.timeout:
-
break
-
# except OSError as e:
-
# if e.winerror == 10035:
-
# # This "error" is expected -- we receive it if timeout is set to zero, and there is no data to read on the socket.
-
# break
-
except Exception as e:
-
print('Unexpected connection error. Reconnecting in a second...', e)
-
self.reconnect(1)
-
return []
-
if not received:
-
print('Connection closed by Twitch. Reconnecting in 5 seconds...')
-
self.reconnect(5)
-
return []
-
buffer += received
-
-
if buffer:
-
# Prepend unparsed data from previous iterations
-
if self.partial:
-
buffer = self.partial + buffer
-
self.partial = []
-
-
# Parse irc messages
-
res = []
-
matches = list(self.re_prog.finditer(buffer))
-
for match in matches:
-
res.append({
-
'name': (match.group(1) or b'').decode(errors='replace'),
-
'command': (match.group(2) or b'').decode(errors='replace'),
-
'params': list(map(lambda p: p.decode(errors='replace'), (match.group(3) or b'').split(b' '))),
-
'trailing': (match.group(4) or b'').decode(errors='replace'),
-
})
-
-
# Save any data we couldn't parse for the next iteration
-
if not matches:
-
self.partial += buffer
-
else:
-
end = matches[-1].end()
-
if end < len(buffer):
-
self.partial = buffer[end:]
-
-
if matches[0].start() != 0:
-
# If we get here, we might have missed a message. pepeW
-
# ⣿⣿⣿⣿⣿⣿⣿⠿⢛⢛⡛⡻⢿⣿⣿⣿⣿⠟⠛⢛⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿
-
# ⣿⣿⣿⣿⢟⢱⡔⡝⣜⣜⢜⢜⡲⡬⡉⢕⢆⢏⢎⢇⢇⣧⡉⠿⣿⣿⣿⣿⣿⣿
-
# ⣿⣿⡟⡱⣸⠸⢝⢅⢆⢖⣜⣲⣵⣴⣱⣈⡣⣋⢣⠭⣢⣒⣬⣕⣄⣝⡻⢿⣿⣿
-
# ⣿⠟⡜⣎⢎⢇⢇⣵⣷⣿⣿⡿⠛⠉⠉⠛⢿⣦⢵⣷⣿⣿⣿⠟⠛⠋⠓⢲⡝⣿
-
# ⢏⢰⢱⣞⢜⢵⣿⣿⣿⣿⣿⠁⠐⠄⠄⠄⠄⢹⣻⣿⣿⣿⠡⠄⠄⠄⠄⠄⠹⣺
-
# ⢕⢜⢕⢕⢵⠹⢿⣿⣿⣿⣿⡀⠸⠗⣀⠄⠄⣼⣻⣿⣿⣿⡀⢾⠆⣀⠄⠄⣰⢳
-
# ⡕⣝⢜⡕⣕⢝⣜⢙⢿⣿⣿⣷⣦⣤⣥⣤⣾⢟⠸⢿⣿⣿⣿⣦⣄⣉⣤⡴⢫⣾
-
# ⡪⡪⣪⢪⢎⢮⢪⡪⡲⢬⢩⢩⢩⠩⢍⡪⢔⢆⢏⡒⠮⠭⡙⡙⠭⢝⣨⣶⣿⣿
-
# ⡪⡪⡎⡮⡪⡎⡮⡪⣪⢣⢳⢱⢪⢝⢜⢜⢕⢝⢜⢎⢧⢸⢱⡹⡍⡆⢿⣿⣿⣿
-
# ⡪⡺⡸⡪⡺⣸⠪⠚⡘⠊⠓⠕⢧⢳⢹⡸⣱⢹⡸⡱⡱⡕⡵⡱⡕⣝⠜⢿⣿⣿
-
# ⡪⡺⡸⡪⡺⢐⢪⢑⢈⢁⢋⢊⠆⠲⠰⠬⡨⡡⣁⣉⠨⡈⡌⢥⢱⠐⢕⣼⣿⣿
-
# ⡪⣪⢣⢫⠪⢢⢅⢥⢡⢅⢅⣑⡨⡑⠅⠕⠔⠔⠄⠤⢨⠠⡰⠠⡂⣎⣼⣿⣿⣿
-
# ⠪⣪⡪⡣⡫⡢⡣⡣⡣⡣⡣⣣⢪⡪⡣⡣⡲⣑⡒⡎⡖⢒⣢⣥⣶⣿⣿⣿⣿⣿
-
# ⢁⢂⠲⠬⠩⣁⣙⢊⡓⠝⠎⠮⠮⠚⢎⡣⡳⠕⡉⣬⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿
-
# ⢐⠐⢌⠐⠅⡂⠄⠄⢌⢉⠩⠡⡉⠍⠄⢄⠢⡁⡢⠠⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿
-
print('either ddarknut fucked up or twitch is bonkers, or both I mean who really knows anything at this point')
-
-
return res
-
-
return []
-
-
def twitch_receive_messages(self):
-
privmsgs = []
-
for irc_message in self.receive_and_parse_data():
-
cmd = irc_message['command']
-
if cmd == 'PRIVMSG':
-
privmsgs.append({
-
'username': irc_message['name'],
-
'message': irc_message['trailing'],
-
})
-
elif cmd == 'PING':
-
self.sock.send(b'PONG :tmi.twitch.tv\r\n')
-
elif cmd == '001':
-
print('Successfully logged in. Joining channel %s.' % self.channel)
-
self.sock.send(('JOIN #%s\r\n' % self.channel).encode())
-
self.login_ok = True
-
elif cmd == 'JOIN':
-
print('Successfully joined channel %s' % irc_message['params'][0])
-
elif cmd == 'NOTICE':
-
print('Server notice:', irc_message['params'], irc_message['trailing'])
-
elif cmd == '002': continue
-
elif cmd == '003': continue
-
elif cmd == '004': continue
-
elif cmd == '375': continue
-
elif cmd == '372': continue
-
elif cmd == '376': continue
-
elif cmd == '353': continue
-
elif cmd == '366': continue
-
else:
-
print('Unhandled irc message:', irc_message)
-
-
if not self.login_ok:
-
# We are still waiting for the initial login message. If we've waited longer than we should, try to reconnect.
-
if time.time() - self.login_timestamp > MAX_TIME_TO_WAIT_FOR_LOGIN:
-
print('No response from Twitch. Reconnecting...')
-
self.reconnect(0)
-
return []
-
-
return privmsgs
-