Added a parser function decorator which allows module functions to be registered as a PRIVMSG callback. Added !botlist and !rollcall for tildeverse's RFC on standardized bot behavior.
This commit is contained in:
parent
84f11b9ddf
commit
8ae7a6970c
53
bot.py
53
bot.py
|
@ -13,12 +13,26 @@ from protocol import IrcProtocol
|
|||
|
||||
import modules
|
||||
|
||||
# global module map, maps registered commands for each loaded module
|
||||
# global module map, maps registered commands and parsers for each loaded module
|
||||
# persists across reloads
|
||||
try: _MOD_MAP
|
||||
except NameError:
|
||||
_MOD_MAP = {}
|
||||
|
||||
def add_module( name ):
|
||||
""" adds a new named empty module definition to the global module map
|
||||
'fixes' passed module name by removing top-level package name
|
||||
returns newly created empty module object """
|
||||
|
||||
mod_name = name[name.find( '.' ) + 1: ]
|
||||
if mod_name not in _MOD_MAP.keys():
|
||||
logging.info( 'Registering \'%s\' module...', # \'%s\' (%s)...',
|
||||
mod_name )
|
||||
|
||||
_MOD_MAP[mod_name] = {'name':mod_name,'cmds':{},'parsers':[]}
|
||||
|
||||
return _MOD_MAP[mod_name]
|
||||
|
||||
def command( name, admin=False ):
|
||||
"""
|
||||
Command registering decorator.
|
||||
|
@ -29,27 +43,32 @@ def command( name, admin=False ):
|
|||
containing the nick that sent, channel it was sent in, and reference to connection object.
|
||||
"""
|
||||
def __deco__( func ):
|
||||
# "fixed" module name. removes top-level 'module' package name
|
||||
mod_name = func.__module__[func.__module__.find( '.' ) + 1: ]
|
||||
if mod_name not in _MOD_MAP.keys():
|
||||
logging.info( 'Registering \'%s\' module commands...', # \'%s\' (%s)...',
|
||||
mod_name ) #, name, func.__name__ )
|
||||
mod = add_module( func.__module__ )
|
||||
|
||||
# set admin flag if needed
|
||||
if admin: func.__dict__['admin'] = True
|
||||
|
||||
if not func.__doc__: func.__doc__ = 'No help available for command'
|
||||
func.__doc__ += ' {}(Module: {})'.format( admin and '(Admin-Only) ' or '', mod_name )
|
||||
func.__doc__ += ' {}(Module: {})'.format( admin and '(Admin-Only) ' or '', mod['name'] )
|
||||
|
||||
if mod_name not in _MOD_MAP.keys():
|
||||
_MOD_MAP[mod_name] = {'cmds':{}}
|
||||
|
||||
_MOD_MAP[mod_name]['cmds'][name.lower()] = func
|
||||
mod['cmds'][name.lower()] = func
|
||||
|
||||
return func #__inner__ # func
|
||||
|
||||
return __deco__
|
||||
|
||||
def parser():
|
||||
"""
|
||||
message parser decorator.
|
||||
decorated function gets passed con, dst, nick, msg on each privmsg
|
||||
"""
|
||||
def __deco__( func ):
|
||||
mod = add_module( func.__module__ )
|
||||
mod['parsers'].append( func )
|
||||
return func
|
||||
|
||||
return __deco__
|
||||
|
||||
class Bot:
|
||||
""" Bot class """
|
||||
def __init__( self, loop, cfg ):
|
||||
|
@ -94,6 +113,13 @@ class Bot:
|
|||
self, parms, {'con':con,'dst':dst,'nick':nick} ) )
|
||||
break
|
||||
|
||||
# callback registered parsers, not for self messages though
|
||||
if nick != self.cfg['user']['nick']:
|
||||
# call each registered parser
|
||||
for v in _MOD_MAP.values():
|
||||
for p in v['parsers']:
|
||||
p( con, dst, nick, msg )
|
||||
|
||||
# core commands
|
||||
|
||||
# join / part
|
||||
|
@ -127,6 +153,7 @@ async def cmd_help( b, p, c ):
|
|||
|
||||
if not p:
|
||||
con.say_to( dst, 'No command specified. See list of commands with \'list\'.' )
|
||||
return
|
||||
|
||||
helpstr = ''
|
||||
|
||||
|
@ -162,7 +189,7 @@ async def cmd_list( b, p, c ):
|
|||
async def cmd_modules( b, p, c ):
|
||||
""" List all currently loaded modules. List commands within using 'list <module>' """
|
||||
cn = c['con']
|
||||
modlist = [m for m in _MOD_MAP.keys()]
|
||||
modlist = list(_MOD_MAP.keys())
|
||||
cn.say_to( c['dst'], '> {}'.format( modlist ) )
|
||||
|
||||
# list active connections
|
||||
|
@ -206,7 +233,7 @@ async def cmd_connect( b, p, c ):
|
|||
new_sv_cfg = {'host':p[1],'port':6697,'name':p[0]}
|
||||
ncon = IrcProtocol( b._loop, con.cfg['usr'] )
|
||||
# add message callback
|
||||
ncon.add_message_callback( b._on_irc_message )
|
||||
ncon.add_message_callback( b._on_priv_message )
|
||||
b._con.append( ncon )
|
||||
b._loop.create_task( ncon.do_connect( new_sv_cfg ) )
|
||||
con.say_to( c['dst'], 'OMW...' )
|
||||
|
|
|
@ -10,7 +10,7 @@ import gzip
|
|||
|
||||
import requests
|
||||
|
||||
from bot import command
|
||||
from bot import command, parser
|
||||
|
||||
# public python GAE api url. thanks sopel
|
||||
PY_APP_URL = 'https://tumbolia-sopel.appspot.com/py/'
|
||||
|
@ -42,6 +42,15 @@ async def cmd_eval( b, p, c ):
|
|||
|
||||
c['con'].say_to( c['dst'], '> {}'.format( rstr ) )
|
||||
|
||||
# parser test
|
||||
@parser()
|
||||
def parse_misc( c, d, n, m ):
|
||||
if m.split()[0].lower() == '!botlist' or m.split()[0].lower() == '!rollcall':
|
||||
pfx = ';' # HAX
|
||||
c.say_to( d, ('> dustbot | Owner: {} | '
|
||||
'Source: https://tildegit.org/slipyx/dustbot | Prefix: \'{}\'. | '
|
||||
'Commands: See \'{}list\'.').format( c.cfg['usr']['owner'],pfx,pfx ) )
|
||||
|
||||
@command( 'rfk' )
|
||||
async def cmd_rfk( b, p, c ):
|
||||
""" Try to find kitten! """
|
||||
|
|
|
@ -177,6 +177,7 @@ class IrcProtocol( asyncio.Protocol ):
|
|||
# helper func for sending a privmsg to specified destination
|
||||
def say_to( self, dst, msg ):
|
||||
msg = msg.replace( '\n', ' ' ).replace( '\r', '' )
|
||||
self.log( '{} >> {}'.format( dst, msg ) )
|
||||
self.send( 'PRIVMSG {} :{}'.format( dst, msg ) )
|
||||
|
||||
# create connection task
|
||||
|
|
Loading…
Reference in New Issue