forked from nortti/tea_cah
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
|
||||
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
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