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
from collections import namedtuple
import logging
import re
import time
@ -16,6 +17,8 @@ class Bot(Controller):
SPECIFIC_RE = r"!(\S+)\s+@(\S+)([\S\s]*)"
GENERIC_RE = r"!(\S+)([\S\s]*)"
ParsedMessage = namedtuple("ParsedMessage", ["command", "argstr"])
def __init__(self, nick):
super().__init__(nick)
@ -60,50 +63,48 @@ class Bot(Controller):
await self.stop()
def noargs(func):
async def wrapper(self, message, args):
if not args:
async def wrapper(self, message, argstr):
if not argstr:
return await func(self, message)
return wrapper
async def on_send(self, message):
parsed = self.parse_message(message.content)
if not parsed:
return
command, args = parsed
wait = []
# general callback (specific set to False)
general = asyncio.ensure_future(
self._callbacks.call((command, False), message, args)
)
specific = self.parse_message(message.content, specific=True)
if specific:
wait.append(self._callbacks.call(
(specific.command, True),
message,
specific.argstr
))
if len(args) > 0:
name = args[0]
args = args[1:]
if name[:1] == "@" and similar(name[1:], self.nick):
logger.debug("Specific command!")
# specific callback (specific set to True)
await self._callbacks.call((command, True), message, args)
general = self.parse_message(message.content, specific=False)
if general:
wait.append(self._callbacks.call(
(general.command, False),
message,
general.argstr
))
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
"""
match = re.fullmatch(self.GENERIC_RE, content)
if not match:
return None
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
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)
if match:
return self.ParsedMessage(match.group(1), match.group(2))
def parse_args(self, text):
"""
@ -188,17 +189,17 @@ class Bot(Controller):
@noargs
async def command_ping(self, 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)
async def command_help(self, message):
if self.help_specific:
await self.room.send(self.help_specific, message.message_id)
await self.room.send(self.help_specific, message.mid)
@noargs
async def command_help_general(self, message):
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
async def command_uptime(self, message):
@ -206,13 +207,13 @@ class Bot(Controller):
startformat = format_time(self.start_time)
deltaformat = format_time_delta(now - self.start_time)
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):
logging.warn(f"Kill attempt by @{mention(message.sender.nick)} in &{self.room.roomname}: {message.content!r}")
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:
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}")
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:
await self.restart()

View file

@ -207,7 +207,7 @@ class Controller:
async def on_send(self, message):
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):
if nick != self.nick:
await self.room.nick(self.nick)

View file

@ -108,6 +108,22 @@ class Session:
self.client_address = client_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
def from_dict(cls, d):
return cls(
@ -188,6 +204,14 @@ class Message():
self.deleted = deleted
self.truncated = truncated
@property
def mid(self):
return self.message_id
@mid.setter
def mid(self, new_mid):
self.message_id = new_mid
@classmethod
def from_dict(cls, d):
return cls(