Added 'admin' attribute for command decorators to specify an admin-only command. Module commands are assigned in a separate dict within the global module map.

This commit is contained in:
Josh K 2018-09-05 04:49:55 -04:00
parent e1fbf0e6ef
commit 7744e6c267
1 changed files with 34 additions and 29 deletions

63
bot.py
View File

@ -13,16 +13,17 @@ from protocol import IrcProtocol
import modules
# global module map, maps registered functions for each loaded module
# global module map, maps registered commands for each loaded module
# persists across reloads
try: _MOD_MAP
except NameError:
_MOD_MAP = {}
def command( name ):
def command( name, admin=False ):
"""
Command registering decorator.
Passed name is name of command, decorated function's docstring becomes command's help message.
Set admin to True to allow only bot's owner/admins to execute.
Function's module name is automatically appended to docstring.
Command function gets passed reference to bot instance, array of params, and context info
containing the nick that sent, channel it was sent in, and reference to connection object.
@ -30,16 +31,20 @@ def command( name ):
def __deco__( func ):
# "fixed" module name. removes top-level 'module' package name
mod_name = func.__module__[func.__module__.find( '.' ) + 1: ]
logging.info( 'Registering \'%s\' module command: \'%s\' (%s)...',
mod_name, name, func.__name__ )
if mod_name not in _MOD_MAP.keys():
logging.info( 'Registering \'%s\' module commands...', # \'%s\' (%s)...',
mod_name ) #, name, func.__name__ )
# 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( 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] = {}
_MOD_MAP[mod_name] = {'cmds':{}}
_MOD_MAP[mod_name][name.lower()] = func
_MOD_MAP[mod_name]['cmds'][name.lower()] = func
return func #__inner__ # func
@ -80,38 +85,43 @@ class Bot:
parms = msg_words[1:]
# check if command is in a registered module
for k, m in _MOD_MAP.items():
if cmd in m.keys():
con.log( '::CMD:: \'{}\' run in ({}) by <{}> with parms: {}'.format(
cmd, chan, nick, parms ) )
if cmd in m['cmds'].keys():
cmd_func = m['cmds'][cmd]
con.log( '::CMD:: \'{}\' run by <{}> in ({}) with parms: {}'.format(
cmd, nick, chan, parms ) )
# if msg was sent as pm,
# change dst to nick so say_to's can respond in pm
if chan == self.cfg['user']['nick']: chan = nick
self._loop.create_task( m[cmd](
# check for admin
if 'admin' in cmd_func.__dict__.keys() and nick != con.cfg['usr']['owner']:
con.say_to( chan, '> You do not have permission to execute this command.' )
break
self._loop.create_task( cmd_func(
self, parms, {'con': con,'dst': chan,'nick': nick} ) )
break
# core commands
# join / part
@command( 'join' )
@command( 'join', True )
async def cmd_join( bot, parms, ctx ):
""" Joins a channel. """
nick = ctx['nick']
#dst = ctx['dst']
con = ctx['con']
if nick == con.cfg['usr']['owner'] and parms \
if parms \
and not (parms[0] in con.cfg['sv']['channels']) and parms[0][0] == '#':
con.send( 'JOIN {}'.format( parms[0] ) )
con.cfg['sv']['channels'].append( parms[0] )
@command( 'part' )
@command( 'part', True )
async def cmd_part( bot, parms, ctx ):
""" Leaves a channel. """
nick = ctx['nick']
con = ctx['con']
if nick == con.cfg['usr']['owner'] and parms \
if parms \
and parms[0] in con.cfg['sv']['channels']:
con.send( 'PART {}'.format( parms[0] ) )
con.cfg['sv']['channels'].remove( parms[0] )
@ -130,8 +140,8 @@ async def cmd_help( b, p, c ):
helpstr = ''
for k, m in _MOD_MAP.items():
if p[0].lower() in m.keys():
helpstr = '> {}: {}'.format( p[0].lower(), ' '.join( m[p[0].lower()].__doc__.split() ) )
if p[0].lower() in m['cmds'].keys():
helpstr = '> {}: {}'.format( p[0].lower(), ' '.join( m['cmds'][p[0].lower()].__doc__.split() ) )
if helpstr:
con.say_to( dst, helpstr )
@ -149,10 +159,10 @@ async def cmd_list( b, p, c ):
for k,v in _MOD_MAP.items():
if p:
if p[0].lower() == k.lower():
cmdlist = list(v.keys())
cmdlist = list(v['cmds'].keys())
break
else:
cmdlist.append( list(v.keys()) )
cmdlist.append( list(v['cmds'].keys()) )
cn.say_to( c['dst'], '> {}'.format( (cmdlist and cmdlist or 'Module not found.') ) )
@ -165,19 +175,17 @@ async def cmd_modules( b, p, c ):
cn.say_to( c['dst'], '> {}'.format( modlist ) )
# list active connections
@command( 'connections' )
@command( 'connections', True )
async def cmd_connections( b, p, c ):
""" List currently connected connections """
if c['nick'] != c['con'].cfg['usr']['owner']: return
conlist = [c.cfg['sv']['name'] for c in b._con]
c['con'].say_to( c['dst'], '> {}'.format( conlist ) )
# reload a specified module
@command( 'reload' )
@command( 'reload', True )
async def cmd_import( bot, p, ctx ):
""" Reloads a previously loaded module. """
con = ctx['con']
if ctx['nick'] != con.cfg['usr']['owner']: return
dst = ctx['dst']
if p and p[0] in _MOD_MAP.keys():
prev_mod = _MOD_MAP.pop( p[0], None )
@ -194,7 +202,7 @@ async def cmd_import( bot, p, ctx ):
else: con.say_to( dst, 'Module not found' )
# connect to a new server
@command( 'connect' )
@command( 'connect', True )
async def cmd_connect( b, p, c ):
"""
Connects to a new network.
@ -202,7 +210,6 @@ async def cmd_connect( b, p, c ):
"""
con = c['con']
if c['nick'] != con.cfg['usr']['owner']: return
if not p and len( p ) < 2: return
new_sv_cfg = {'host':p[1],'port':6697,'name':p[0]}
@ -214,20 +221,18 @@ async def cmd_connect( b, p, c ):
con.say_to( c['dst'], 'OMW...' )
# disconnect from currently connected server
@command( 'disconnect' )
@command( 'disconnect', True )
async def cmd_discon( b, p, c ):
""" Disconnects from currently connected network """
con = c['con']
if c['nick'] != con.cfg['usr']['owner']: return
con._stop = True
con.send( 'QUIT :POOF' )
b._con.remove( con )
# list of names on chan
@command( 'names' )
@command( 'names', True )
async def cmd_names( b, p, c ):
cn = c['con']
if c['nick'] != cn.cfg['usr']['owner']: return
nmstr = ''
for k,v in cn.names.items():
if c['dst'] in v: