Add start of line handling, don't log PONGs

This commit is contained in:
Juhani Haverinen 2017-09-05 12:47:47 +03:00
parent 4c7fe56950
commit 36ab28cd71
2 changed files with 146 additions and 10 deletions

View File

@ -7,6 +7,8 @@ from collections import namedtuple
import channel
from constants import logmessage_types, internal_submessage_types, controlmessage_types
import line_handling
Server = namedtuple('Server', ['host', 'port'])
# ServerThread(server, control_socket)
@ -28,15 +30,17 @@ class ServerThread(threading.Thread):
with self.server_socket_write_lock:
self.server_socket.sendall(line + b'\r\n')
self.logging_channel.send((logmessage_types.sent, line.decode(encoding = 'utf-8', errors = 'replace')))
# Don't log PONGs
if not (len(line) >= 5 and line[:5] == b'PONG '):
self.logging_channel.send((logmessage_types.sent, line.decode(encoding = 'utf-8', errors = 'replace')))
def handle_line(self, line):
command, _, arguments = line.partition(b' ')
if command.upper() == b'PING':
self.send_line_raw(b'PONG ' + arguments)
else:
# TODO: implement line handling
self.logging_channel.send((logmessage_types.received, line.decode(encoding = 'utf-8', errors = 'replace')))
line_handling.handle_line(line, irc = self.api)
def mainloop(self):
# Register both the server socket and the control channel to or polling object
@ -80,7 +84,8 @@ class ServerThread(threading.Thread):
self.send_line_raw(line)
else:
self.logging_channel.send((logmessage_types.internal, internal_submessage_types.error))
error_message = 'Unknown control message: %s' % repr((command_type, *arguments))
self.logging_channel.send((logmessage_types.internal, internal_submessage_types.error, error_message))
else:
assert False #unreachable
@ -92,10 +97,13 @@ class ServerThread(threading.Thread):
self.server_socket = socket.create_connection(address)
except ConnectionRefusedError:
# Tell controller we failed
self.logging_channel.send((logmessage_types.internal, internal_submessage_types.error))
self.logging_channel.send((logmessage_types.internal, internal_submessage_types.error, "Can't connect to %s:%s" % address))
self.logging_channel.send((logmessage_types.internal, internal_submessage_types.quit))
return
# Create an API object to give to outside line handler
self.api = line_handling.API(self)
# Run initialization
# TODO: read nick/username/etc. from a config
self.send_line_raw(b'NICK HynneFlip')
@ -130,16 +138,20 @@ if __name__ == '__main__':
data = logging_channel.recv(blocking = False)
if data == None:
break
message_type, message_data = data
message_type, *message_data = data
if message_type == logmessage_types.sent:
print('>' + message_data)
assert len(message_data) == 1
print('>' + message_data[0])
elif message_type == logmessage_types.received:
print('<' + message_data)
assert len(message_data) == 1
print('<' + message_data[0])
elif message_type == logmessage_types.internal:
if message_data == internal_submessage_types.quit:
if message_data[0] == internal_submessage_types.quit:
assert len(message_data) == 1
print('--- Quit')
elif message_data == internal_submessage_types.error:
print('--- Error')
elif message_data[0] == internal_submessage_types.error:
assert len(message_data) == 2
print('--- Error', message_data[1])
else:
print('--- ???', message_data)
else:

124
line_handling.py Normal file
View File

@ -0,0 +1,124 @@
import constants
class API:
def __init__(self, serverthread_object):
# We need to access the internal functions of the ServerThread object in order to send lines etc.
self.serverthread_object = serverthread_object
def send(self, line):
self.serverthread_object.send_line_raw(line)
def msg(self, recipient, message):
"""Make sending PRIVMSGs much nicer"""
line = 'PRIVMSG ' + recipient + ' :' + message
self.serverthread_object.send_line_raw(line)
def error(self, message):
self.serverthread_object.logging_channel.send((constants.logmessage_types.internal, constants.internal_submessage_types.error, message))
class LineParsingError(Exception): None
# parse_line(line) → prefix, command, arguments
# Split the line into its component parts
def parse_line(line):
def read_byte():
# Read one byte and advance the index
nonlocal line, index
if eol():
raise LineParsingError
byte = line[index]
index += 1
return byte
def peek_byte():
# Look at current byte, don't advance index
nonlocal line, index
if eol():
raise LineParsingError
return line[index]
def eol():
# Test if we've reached the end of the line
nonlocal line, index
return index >= len(line)
def skip_space():
# Skip until we run into a non-space character or eol.
while not eol() and peek_byte() == ord(' '):
read_byte()
def read_until_space():
nonlocal line, index
if eol():
raise LineParsingError
# Try to find a space
until = line[index:].find(b' ')
if until == -1:
# Space not found, read until end of line
until = len(line)
else:
# Space found, add current index to it to get right index
until += index
# Slice line upto the point of next space / end and update index
data = line[index:until]
index = until
return data
def read_until_end():
nonlocal line, index
if eol():
raise LineParsingError
# Read all of the data, and make index point to eol
data = line[index:]
index = len(line)
return data
index = 0
prefix = None
command = None
arguments = []
if peek_byte() == ord(':'):
read_byte()
prefix = read_until_space()
skip_space()
command = read_until_space()
skip_space()
while not eol():
if peek_byte() == ord(':'):
read_byte()
argument = read_until_end()
else:
argument = read_until_space()
arguments.append(argument)
skip_space()
return prefix, command, arguments
def handle_line(line, *, irc):
try:
prefix, command, arguments = parse_line(line)
except LineParsingError:
irc.error("Cannot parse line" + line.decode(encoding = 'utf-8', errors = 'replace'))
# TODO: handle line