Improve bot command parsing

Further improvements:
 - add message.mid or session.uid or session.sid
 - use "messages" instead of "log" and "sessions" instead of
   "listing" where they're only lists
This commit is contained in:
Joscha 2017-09-08 11:16:44 +00:00
parent 5b0f078f7a
commit da84c6685e
3 changed files with 63 additions and 38 deletions

View file

@ -1,4 +1,5 @@
import asyncio import asyncio
from collections import namedtuple
import logging import logging
import re import re
import time import time
@ -16,6 +17,8 @@ class Bot(Controller):
SPECIFIC_RE = r"!(\S+)\s+@(\S+)([\S\s]*)" SPECIFIC_RE = r"!(\S+)\s+@(\S+)([\S\s]*)"
GENERIC_RE = r"!(\S+)([\S\s]*)" GENERIC_RE = r"!(\S+)([\S\s]*)"
ParsedMessage = namedtuple("ParsedMessage", ["command", "argstr"])
def __init__(self, nick): def __init__(self, nick):
super().__init__(nick) super().__init__(nick)
@ -60,50 +63,48 @@ class Bot(Controller):
await self.stop() await self.stop()
def noargs(func): def noargs(func):
async def wrapper(self, message, args): async def wrapper(self, message, argstr):
if not args: if not argstr:
return await func(self, message) return await func(self, message)
return wrapper return wrapper
async def on_send(self, message): async def on_send(self, message):
parsed = self.parse_message(message.content) wait = []
if not parsed:
return
command, args = parsed
# general callback (specific set to False) specific = self.parse_message(message.content, specific=True)
general = asyncio.ensure_future( if specific:
self._callbacks.call((command, False), message, args) wait.append(self._callbacks.call(
) (specific.command, True),
message,
specific.argstr
))
if len(args) > 0: general = self.parse_message(message.content, specific=False)
name = args[0] if general:
args = args[1:] wait.append(self._callbacks.call(
if name[:1] == "@" and similar(name[1:], self.nick): (general.command, False),
logger.debug("Specific command!") message,
# specific callback (specific set to True) general.argstr
await self._callbacks.call((command, True), message, args) ))
await general if wait:
await asyncio.wait(wait)
def parse_message(self, content): def parse_message(self, content, specific=True):
""" """
(command, args) = parse_message(content) ParsedMessage = parse_message(content)
Returns None, not a (None, None) tuple, when message could not be parsed Returns None, not a (None, None) tuple, when message could not be parsed
""" """
if specific:
match = re.fullmatch(self.SPECIFIC_RE, content)
if match and similar(match.group(2), self.nick):
return self.ParsedMessage(match.group(1), match.group(3))
else:
match = re.fullmatch(self.GENERIC_RE, content) match = re.fullmatch(self.GENERIC_RE, content)
if not match: if match:
return None return self.ParsedMessage(match.group(1), match.group(2))
command = match.group(1)
argstr = match.group(2)
args = self.parse_args(argstr)
logger.debug(f"Parsed command. command={command!r}, args={args!r}")
return command, args
def parse_args(self, text): def parse_args(self, text):
""" """
@ -188,17 +189,17 @@ class Bot(Controller):
@noargs @noargs
async def command_ping(self, message): async def command_ping(self, message):
if self.ping_message: if self.ping_message:
await self.room.send(self.ping_message, message.message_id) await self.room.send(self.ping_message, message.mid)
@noargs # TODO: specific command help (!help @bot ping) @noargs # TODO: specific command help (!help @bot ping)
async def command_help(self, message): async def command_help(self, message):
if self.help_specific: if self.help_specific:
await self.room.send(self.help_specific, message.message_id) await self.room.send(self.help_specific, message.mid)
@noargs @noargs
async def command_help_general(self, message): async def command_help_general(self, message):
if self.help_general is not None: if self.help_general is not None:
await self.room.send(self.help_general, message.message_id) await self.room.send(self.help_general, message.mid)
@noargs @noargs
async def command_uptime(self, message): async def command_uptime(self, message):
@ -206,13 +207,13 @@ class Bot(Controller):
startformat = format_time(self.start_time) startformat = format_time(self.start_time)
deltaformat = format_time_delta(now - self.start_time) deltaformat = format_time_delta(now - self.start_time)
text = f"/me has been up since {startformat} ({deltaformat})" text = f"/me has been up since {startformat} ({deltaformat})"
await self.room.send(text, message.message_id) await self.room.send(text, message.mid)
async def command_kill(self, message, args): async def command_kill(self, message, args):
logging.warn(f"Kill attempt by @{mention(message.sender.nick)} in &{self.room.roomname}: {message.content!r}") logging.warn(f"Kill attempt by @{mention(message.sender.nick)} in &{self.room.roomname}: {message.content!r}")
if self.kill_message is not None: if self.kill_message is not None:
await self.room.send(self.kill_message, message.message_id) await self.room.send(self.kill_message, message.mid)
if self.killable: if self.killable:
await self.stop() await self.stop()
@ -221,7 +222,7 @@ class Bot(Controller):
logging.warn(f"Restart attempt by @{mention(message.sender.nick)} in &{self.room.roomname}: {message.content!r}") logging.warn(f"Restart attempt by @{mention(message.sender.nick)} in &{self.room.roomname}: {message.content!r}")
if self.restart_message is not None: if self.restart_message is not None:
await self.room.send(self.restart_message, message.message_id) await self.room.send(self.restart_message, message.mid)
if self.restartable: if self.restartable:
await self.restart() await self.restart()

View file

@ -207,7 +207,7 @@ class Controller:
async def on_send(self, message): async def on_send(self, message):
pass pass
async def on_snapshot(self, user_id, session_id, version, listing, log, nick=None, async def on_snapshot(self, user_id, session_id, version, sessions, messages, nick=None,
pm_with_nick=None, pm_with_user_id=None): pm_with_nick=None, pm_with_user_id=None):
if nick != self.nick: if nick != self.nick:
await self.room.nick(self.nick) await self.room.nick(self.nick)

View file

@ -108,6 +108,22 @@ class Session:
self.client_address = client_address self.client_address = client_address
self.real_address = real_address self.real_address = real_address
@property
def uid(self):
return self.user_id
@uid.setter
def uid(self, new_uid):
self.user_id = new_uid
@property
def sid(self):
return self.session_id
@sid.setter
def sid(self, new_sid):
self.session_id = new_sid
@classmethod @classmethod
def from_dict(cls, d): def from_dict(cls, d):
return cls( return cls(
@ -188,6 +204,14 @@ class Message():
self.deleted = deleted self.deleted = deleted
self.truncated = truncated self.truncated = truncated
@property
def mid(self):
return self.message_id
@mid.setter
def mid(self, new_mid):
self.message_id = new_mid
@classmethod @classmethod
def from_dict(cls, d): def from_dict(cls, d):
return cls( return cls(