Add start of line handling, don't log PONGs
This commit is contained in:
parent
4c7fe56950
commit
36ab28cd71
2 changed files with 146 additions and 10 deletions
32
ircbot.py
32
ircbot.py
|
@ -7,6 +7,8 @@ from collections import namedtuple
|
||||||
import channel
|
import channel
|
||||||
from constants import logmessage_types, internal_submessage_types, controlmessage_types
|
from constants import logmessage_types, internal_submessage_types, controlmessage_types
|
||||||
|
|
||||||
|
import line_handling
|
||||||
|
|
||||||
Server = namedtuple('Server', ['host', 'port'])
|
Server = namedtuple('Server', ['host', 'port'])
|
||||||
|
|
||||||
# ServerThread(server, control_socket)
|
# ServerThread(server, control_socket)
|
||||||
|
@ -28,15 +30,17 @@ class ServerThread(threading.Thread):
|
||||||
with self.server_socket_write_lock:
|
with self.server_socket_write_lock:
|
||||||
self.server_socket.sendall(line + b'\r\n')
|
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):
|
def handle_line(self, line):
|
||||||
command, _, arguments = line.partition(b' ')
|
command, _, arguments = line.partition(b' ')
|
||||||
if command.upper() == b'PING':
|
if command.upper() == b'PING':
|
||||||
self.send_line_raw(b'PONG ' + arguments)
|
self.send_line_raw(b'PONG ' + arguments)
|
||||||
else:
|
else:
|
||||||
# TODO: implement line handling
|
|
||||||
self.logging_channel.send((logmessage_types.received, line.decode(encoding = 'utf-8', errors = 'replace')))
|
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):
|
def mainloop(self):
|
||||||
# Register both the server socket and the control channel to or polling object
|
# 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)
|
self.send_line_raw(line)
|
||||||
|
|
||||||
else:
|
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:
|
else:
|
||||||
assert False #unreachable
|
assert False #unreachable
|
||||||
|
@ -92,10 +97,13 @@ class ServerThread(threading.Thread):
|
||||||
self.server_socket = socket.create_connection(address)
|
self.server_socket = socket.create_connection(address)
|
||||||
except ConnectionRefusedError:
|
except ConnectionRefusedError:
|
||||||
# Tell controller we failed
|
# 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))
|
self.logging_channel.send((logmessage_types.internal, internal_submessage_types.quit))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Create an API object to give to outside line handler
|
||||||
|
self.api = line_handling.API(self)
|
||||||
|
|
||||||
# Run initialization
|
# Run initialization
|
||||||
# TODO: read nick/username/etc. from a config
|
# TODO: read nick/username/etc. from a config
|
||||||
self.send_line_raw(b'NICK HynneFlip')
|
self.send_line_raw(b'NICK HynneFlip')
|
||||||
|
@ -130,16 +138,20 @@ if __name__ == '__main__':
|
||||||
data = logging_channel.recv(blocking = False)
|
data = logging_channel.recv(blocking = False)
|
||||||
if data == None:
|
if data == None:
|
||||||
break
|
break
|
||||||
message_type, message_data = data
|
message_type, *message_data = data
|
||||||
if message_type == logmessage_types.sent:
|
if message_type == logmessage_types.sent:
|
||||||
print('>' + message_data)
|
assert len(message_data) == 1
|
||||||
|
print('>' + message_data[0])
|
||||||
elif message_type == logmessage_types.received:
|
elif message_type == logmessage_types.received:
|
||||||
print('<' + message_data)
|
assert len(message_data) == 1
|
||||||
|
print('<' + message_data[0])
|
||||||
elif message_type == logmessage_types.internal:
|
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')
|
print('--- Quit')
|
||||||
elif message_data == internal_submessage_types.error:
|
elif message_data[0] == internal_submessage_types.error:
|
||||||
print('--- Error')
|
assert len(message_data) == 2
|
||||||
|
print('--- Error', message_data[1])
|
||||||
else:
|
else:
|
||||||
print('--- ???', message_data)
|
print('--- ???', message_data)
|
||||||
else:
|
else:
|
||||||
|
|
124
line_handling.py
Normal file
124
line_handling.py
Normal 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
|
Loading…
Reference in a new issue