Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cardinal.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def setup_logging(config=None):
'urbandict',
'seen'
])
spec.add_option('cmd_prefix', str, '.')
spec.add_option('blacklist', dict, {})
spec.add_option('logging', dict, None)

Expand Down Expand Up @@ -129,6 +130,7 @@ def setup_logging(config=None):
config['username'],
config['realname'],
config['plugins'],
config['cmd_prefix'],
config['blacklist'],
config['storage'])

Expand Down
4 changes: 4 additions & 0 deletions cardinal/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def signedOn(self):
# Setup PluginManager
self.plugin_manager = PluginManager(self,
self.factory.plugins,
self.factory.cmd_prefix,
self.factory.blacklist)

if self.factory.server_commands:
Expand Down Expand Up @@ -639,6 +640,7 @@ def __init__(self,
username,
realname,
plugins,
cmd_prefix,
blacklist,
storage):
"""Boots the bot, triggers connection, and initializes logging.
Expand All @@ -653,6 +655,7 @@ def __init__(self,
username -- A string with the ident to be used.
realname -- A string containing the real name field.
plugins -- A list of plugins to load on boot.
cmd_prefix -- A string containing the command prefix.
blacklist -- A dict mapping plugins to lists of blacklisted channels.
storage -- A string containing path to storage directory.
"""
Expand All @@ -666,6 +669,7 @@ def __init__(self,
self.username = username
self.realname = realname
self.plugins = plugins
self.cmd_prefix = cmd_prefix
self.blacklist = blacklist
self.storage_path = storage

Expand Down
21 changes: 15 additions & 6 deletions cardinal/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,20 @@
class PluginManager:
"""Keeps track of, loads, and unloads plugins."""

COMMAND_REGEX = re.compile(r'\.([A-Za-z0-9_-]+)\s?.*$')
COMMAND_REGEX = None # set during __init__()
"""Regex for matching standard commands.

This will check for anything beginning with a period (.) followed by any
alphanumeric character, then whitespace, then any character(s). This means
registered commands will be found so long as the registered command is
alphanumeric (or either _ or -), and any additional arguments will be up to
the plugin for handling.
This will check for anything beginning with a command prefix as defined by
the config file, followed by any alphanumeric character, then whitespace,
then any character(s). This means registered commands will be found so long
as the registered command is alphanumeric (or either _ or -), and any
additional arguments will be up to the plugin for handling.
"""

def __init__(self,
cardinal,
plugins,
cmd_prefix,
blacklist,
_plugin_module_import_prefix='plugins',
_plugin_module_directory=None):
Expand All @@ -56,6 +57,10 @@ def __init__(self,
self.logger = logging.getLogger(__name__)
self.cardinal = cardinal
self._blacklist = blacklist

# Allow for user-defined command prefix (default is '.')
self._cmd_prefix = cmd_prefix
self.COMMAND_REGEX = re.compile(r'[{}]([A-Za-z0-9_-]+)\s?.*$'.format(cmd_prefix))

# Module name from which plugins are imported. This exists to assist
# in unit testing.
Expand Down Expand Up @@ -187,6 +192,10 @@ def _instantiate_plugin(self, module, config=None):
kwargs['cardinal'] = self.cardinal
elif param == 'config':
kwargs['config'] = config
elif param == 'cmd_prefix':
# The plugin needs the user-defined cmd prefix for whatever
# reason (e.g. help):
kwargs['cmd_prefix'] = self._cmd_prefix
else:
raise PluginError(
"Unknown parameter {} in entrypoint signature"
Expand Down
2 changes: 2 additions & 0 deletions config/config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
"youtube"
],

"cmd_prefix": "."

"logging": {
"version": 1,

Expand Down
2 changes: 1 addition & 1 deletion plugins/8ball/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class Magic8BallPlugin:
@command(['8', '8ball'])
@help("Ask the might 8-ball a question.")
@help("Syntax: .8 <question>")
@help("Syntax: @8 <question>")
@defer.inlineCallbacks
def answer(self, cardinal, user, channel, msg):
if not (msg.endswith("?") and len(msg.split()) > 1):
Expand Down
20 changes: 10 additions & 10 deletions plugins/admin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def is_admin(self, user):
@command('eval')
@help("A super dangerous command that runs eval() on the input. "
"(admin only)")
@help("Syntax: .eval <command>")
@help("Syntax: @eval <command>")
def eval(self, cardinal, user, channel, msg):
if self.is_admin(user):
command = ' '.join(msg.split()[1:])
Expand All @@ -60,7 +60,7 @@ def eval(self, cardinal, user, channel, msg):
@command('exec')
@help("A dangerous command that runs exec() on the input. " +
"(admin only)")
@help("Syntax: .exec <command>")
@help("Syntax: @exec <command>")
def execute(self, cardinal, user, channel, msg):
if self.is_admin(user):
command = ' '.join(msg.split()[1:])
Expand All @@ -76,7 +76,7 @@ def execute(self, cardinal, user, channel, msg):
@command(['load', 'reload'])
@help("If no plugins are given after the command, reload all plugins. "
"Otherwise, load (or reload) the selected plugins. (admin only)")
@help("Syntax: .load [plugin [plugin ...]]")
@help("Syntax: @load [plugin [plugin ...]]")
def load_plugins(self, cardinal, user, channel, msg):
if self.is_admin(user):
cardinal.sendMsg(channel, "%s: Loading plugins..." % user.nick)
Expand Down Expand Up @@ -105,7 +105,7 @@ def load_plugins(self, cardinal, user, channel, msg):

@command('unload')
@help("Unload selected plugins. (admin only)")
@help("Syntax: .unload <plugin [plugin ...]>")
@help("Syntax: @unload <plugin [plugin ...]>")
def unload_plugins(self, cardinal, user, channel, msg):
nick = user.nick

Expand Down Expand Up @@ -135,7 +135,7 @@ def unload_plugins(self, cardinal, user, channel, msg):

@command('disable')
@help("Disable plugins in a channel. (admin only)")
@help("Syntax: .disable <plugin> <channel [channel ...]>")
@help("Syntax: @disable <plugin> <channel [channel ...]>")
def disable_plugins(self, cardinal, user, channel, msg):
if not self.is_admin(user):
return
Expand Down Expand Up @@ -164,7 +164,7 @@ def disable_plugins(self, cardinal, user, channel, msg):

@command('enable')
@help("Enable plugins in a channel. (admin only)")
@help("Syntax: .enable <plugin> <channel [channel ...]>")
@help("Syntax: @enable <plugin> <channel [channel ...]>")
def enable_plugins(self, cardinal, user, channel, msg):
if not self.is_admin(user):
return
Expand Down Expand Up @@ -202,7 +202,7 @@ def enable_plugins(self, cardinal, user, channel, msg):

@command('join')
@help("Joins selected channels. (admin only)")
@help("Syntax: .join <channel [channel ...]>")
@help("Syntax: @join <channel [channel ...]>")
def join(self, cardinal, user, channel, msg):
if self.is_admin(user):
channels = msg.split()
Expand All @@ -212,7 +212,7 @@ def join(self, cardinal, user, channel, msg):

@command('part')
@help("Parts selected channels. (admin only)")
@help("Syntax: .join <channel [channel ...]>")
@help("Syntax: @join <channel [channel ...]>")
def part(self, cardinal, user, channel, msg):
if not self.is_admin(user):
return
Expand All @@ -228,15 +228,15 @@ def part(self, cardinal, user, channel, msg):
@command('quit')
@help("Quits the network with a quit message, if one is defined. "
"(admin only)")
@help("Syntax: .quit [message]")
@help("Syntax: @quit [message]")
def quit(self, cardinal, user, channel, msg):
if self.is_admin(user):
cardinal.disconnect(' '.join(msg.split(' ')[1:]))

@command('dbg_quit')
@help("Quits the network without setting disconnect flag "
"(for testing reconnection, admin only)")
@help("Syntax: .dbg_quit")
@help("Syntax: @dbg_quit")
def debug_quit(self, cardinal, user, channel, msg):
if self.is_admin(user):
cardinal.quit('Debug disconnect')
Expand Down
2 changes: 1 addition & 1 deletion plugins/crypto/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __init__(self, cardinal, config):

@command('crypto')
@help('Check the price of a cryptocurrency')
@help('Syntax: .crypto <cryptocurrency,...> [price currency]')
@help('Syntax: @crypto <cryptocurrency,...> [price currency]')
@defer.inlineCallbacks
def crypto(self, cardinal, user, channel, message):
nick = user.nick
Expand Down
2 changes: 1 addition & 1 deletion plugins/github/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def __init__(self, cardinal, config):

@command('issue')
@help("Find a Github repo or issue (or combination thereof)")
@help("Syntax: .issue [username/repository] <id or search query>")
@help("Syntax: @issue [username/repository] <id or search query>")
@defer.inlineCallbacks
def search(self, cardinal, user, channel, msg):
# Grab the search query
Expand Down
2 changes: 1 addition & 1 deletion plugins/google/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def __init__(self, config):

@command(['google', 'lmgtfy', 'g'])
@help("Returns the URL of the top result for a given search query")
@help("Syntax: .google <query>")
@help("Syntax: @google <query>")
def query(self, cardinal, user, channel, msg):
# gets search string from message, and makes it url safe
try:
Expand Down
21 changes: 19 additions & 2 deletions plugins/help/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@
from cardinal.decorators import command, help


# Command prefix placeholder to look for when performing string replace on
# the syntax help message.
PREFIX_PLACEHOLDER = '@'


class HelpPlugin:
def __init__(self, cmd_prefix):
self._cmd_prefix = cmd_prefix

# Gets a list of admins from the admin plugin instantiated within the
# Cardinal instance, if exists
def _get_admins(self, cardinal):
Expand Down Expand Up @@ -76,11 +84,19 @@ def _pretty_uptime(self, seconds):

return uptime

# Replace the command prefix placeholder (@) with the user-defined prefix
def _replace_prefix(self, help_msg):
if isinstance(help_msg, list):
correct_help_msg = [line.replace(PREFIX_PLACEHOLDER, self._cmd_prefix) for line in help_msg]
else:
correct_help_msg = help_msg.replace(PREFIX_PLACEHOLDER, self._cmd_prefix)
return correct_help_msg

# Give the user a list of valid commands in the bot if no command is
# provided. If a valid command is provided, return its help text
@command(['help'])
@help("Shows loaded commands or a specific command's help.")
@help("Syntax: .help [command]")
@help("Syntax: @help [command]")
def cmd_help(self, cardinal, user, channel, msg):
parameters = msg.split()
if len(parameters) == 1:
Expand All @@ -91,6 +107,7 @@ def cmd_help(self, cardinal, user, channel, msg):
else:
command = parameters[1]
help = self._get_command_help(cardinal, command)
help = self._replace_prefix(help)
if isinstance(help, list):
for help_line in help:
cardinal.sendMsg(channel, help_line)
Expand All @@ -104,7 +121,7 @@ def cmd_help(self, cardinal, user, channel, msg):
# Sends some basic meta information about the bot
@command('info')
@help("Gives some basic information about the bot.")
@help("Syntax: .info")
@help("Syntax: @info")
def cmd_info(self, cardinal, user, channel, msg):
admins = self._get_admins(cardinal)
meta = self._get_meta(cardinal)
Expand Down
4 changes: 2 additions & 2 deletions plugins/lastfm/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def _connect_or_create_db(self, cardinal):

@command('setlastfm')
@help(["Sets the default Last.fm username for your nick.",
"Syntax: .setlastfm <username>"])
"Syntax: @setlastfm <username>"])
def set_user(self, cardinal, user, channel, msg):
if not self.conn:
cardinal.sendMsg(
Expand Down Expand Up @@ -106,7 +106,7 @@ def _get_or_update_username(self, nick, vhost, username):
@command(['np', 'nowplaying'])
@help("Get the Last.fm track currently played by a user (defaults to "
"username set with .setlastfm)")
@help("Syntax: .np [Last.fm username]")
@help("Syntax: @np [Last.fm username]")
@defer.inlineCallbacks
def now_playing(self, cardinal, user, channel, msg):
# Open the cursor for the query to find a saved Last.fm username
Expand Down
8 changes: 4 additions & 4 deletions plugins/movies/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,21 +132,21 @@ def get_output_format(self, channel):

@command('movie')
@help('Get the first movie IMDb result for a given search')
@help('Syntax: .movie <search query>')
@help('Syntax: @movie <search query>')
@defer.inlineCallbacks
def movie(self, cardinal, user, channel, msg):
yield self.imdb(cardinal, user, channel, msg, result_type='movie')

@command('show')
@help('Get the first TV show IMDb result for a given search')
@help('Syntax: .show <search query>')
@help('Syntax: @show <search query>')
@defer.inlineCallbacks
def show(self, cardinal, user, channel, msg):
yield self.imdb(cardinal, user, channel, msg, result_type='series')

@command(['omdb', 'imdb'])
@help('Get the first IMDb result for a given search')
@help('Syntax: .imdb <search query>')
@help('Syntax: @imdb <search query>')
@defer.inlineCallbacks
def imdb(self, cardinal, user, channel, msg, result_type=None):
# Before we do anything, let's make sure we'll be able to query omdb.
Expand Down Expand Up @@ -212,7 +212,7 @@ def imdb(self, cardinal, user, channel, msg, result_type=None):

@command('search')
@help('Return IMDb search results (use .imdb for a single title)')
@help('Syntax: .search <search query>')
@help('Syntax: @search <search query>')
@defer.inlineCallbacks
def search(self, cardinal, user, channel, msg):
if self.api_key is None:
Expand Down
2 changes: 1 addition & 1 deletion plugins/random/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def parse_roll(arg):
class RandomPlugin:
@command('roll')
@help("Roll dice")
@help("Syntax: .roll #d# (e.g. .roll 2d6)")
@help("Syntax: @roll #d# (e.g. .roll 2d6)")
def roll(self, cardinal, user, channel, msg):
args = msg.split(' ')
args.pop(0)
Expand Down
2 changes: 1 addition & 1 deletion plugins/remind/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def __init__(self):

@command('remind')
@help("Sends a reminder after a set time.")
@help("Syntax: .remind <minutes> <message>")
@help("Syntax: @remind <minutes> <message>")
def remind(self, cardinal, user, channel, msg):
message = msg.split(None, 2)
if len(message) < 3:
Expand Down
4 changes: 2 additions & 2 deletions plugins/seen/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def format_seen(self, nick):

@command('seen')
@help("Returns the last time a user was seen, and their last action.")
@help("Syntax: .seen <user>")
@help("Syntax: @seen <user>")
def seen(self, cardinal, user, channel, msg):
try:
nick = msg.split(' ')[1]
Expand All @@ -225,7 +225,7 @@ def seen(self, cardinal, user, channel, msg):

@command('tell')
@help("Tell an offline user something when they come online.")
@help("Syntax: .tell <nick> <message>")
@help("Syntax: @tell <nick> <message>")
def tell(self, cardinal, user, channel, msg):
try:
nick, message = msg.split(' ', 2)[1:]
Expand Down
4 changes: 2 additions & 2 deletions plugins/ticker/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ def send_prediction(

@command('stock')
@help("Check the latest price of a stock")
@help("Syntax: .stock <stock symbol>")
@help("Syntax: @stock <stock symbol>")
@defer.inlineCallbacks
def stock(self, cardinal, user, channel, msg):
nick = user.nick # other values may not exist for relayed users
Expand Down Expand Up @@ -381,7 +381,7 @@ def stock_relayed(self, cardinal, user, channel, msg):

@command('predict')
@help("Predict a stock price at the next market open/close")
@help("Syntax: .predict <stock> [-]<X>% | .predict <stock> $<X>")
@help("Syntax: @predict <stock> [-]<X>% | @predict <stock> $<X>")
@defer.inlineCallbacks
def predict(self, cardinal, user, channel, msg):
nick = user.nick
Expand Down
Loading