# -*- coding: utf-8 -*-
# Copyright (C) 2013-2015 Samuel Damashek, Peter Foley, James Forcier, Srijay Kasturi, Reed Koser, Christopher Reffett, and Fox Wilson
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import logging
import re
import sys
from . import arguments, command, hook, orm, web
[docs]def handle_chanserv(args):
args.send("%s %s" % (args.cmd, " ".join(args.args)), target="ChanServ")
[docs]def toggle_logging(level):
if logging.getLogger().getEffectiveLevel() == level:
return False
else:
logging.getLogger().setLevel(level)
return True
[docs]def toggle_module(type, name, enable=True):
if not name:
return "Missing argument."
toggle = "enable_%s" % type if enable else "disable_%s" % type
return getattr(sys.modules["cslbot.helpers.%s" % type].registry, toggle)(name[0])
[docs]def handle_disable(args):
if args.cmd == "kick":
if not args.handler.kick_enabled:
args.send("Kick already disabled.")
else:
args.handler.kick_enabled = False
args.send("Kick disabled.")
elif args.cmd in ["command", "hook"]:
args.send(toggle_module(args.cmd, args.args, False))
elif args.cmd == "logging":
if toggle_logging(logging.INFO):
args.send("Logging disabled.")
else:
args.send("logging already disabled.")
elif args.cmd == "chanlog":
if args.handler.log_to_ctrlchan:
args.handler.log_to_ctrlchan = False
args.send("Control channel logging disabled.")
else:
args.send("Control channel logging is already disabled.")
[docs]def handle_enable(args):
if args.cmd == "kick":
if args.handler.kick_enabled:
args.send("Kick already enabled.")
else:
args.handler.kick_enabled = True
args.send("Kick enabled.")
elif args.cmd in ["command", "hook"]:
args.send(toggle_module(args.cmd, args.args))
elif args.cmd == "all":
if not args.args:
args.send("Missing argument.")
elif args.args[0] == "commands":
args.send(command.registry.enable_command(args.cmd))
elif args.args[0] == "hooks":
args.send(hook.registry.enable_hook(args.cmd))
else:
args.send("Invalid argument.")
elif args.cmd == "logging":
if toggle_logging(logging.DEBUG):
args.send("Logging enabled.")
else:
args.send("logging already enabled.")
elif args.cmd == "chanlog":
if not args.handler.log_to_ctrlchan:
args.handler.log_to_ctrlchan = True
args.send("Control channel logging enabled.")
else:
args.send("Control channel logging is already enabled.")
[docs]def handle_guard(args):
if args.nick in args.handler.guarded:
args.send("Already guarding %s" % args.nick)
else:
args.handler.guarded.append(args.nick)
args.send("Guarding %s" % args.nick)
[docs]def handle_unguard(args):
if args.nick not in args.handler.guarded:
args.send("%s is not being guarded" % args.nick)
else:
args.handler.guarded.remove(args.nick)
args.send("No longer guarding %s" % args.nick)
[docs]def handle_show_pending(args):
table = getattr(orm, args.cmd.capitalize())
pending = args.db.query(table).filter(table.accepted == 0).all()
if pending:
show_pending_items(args.cmd, pending, args.send)
else:
args.send("No pending %s." % args.cmd)
[docs]def handle_show(args):
if args.cmd == "guarded":
if args.handler.guarded:
args.send(", ".join(args.handler.guarded))
else:
args.send("Nobody is guarded.")
elif args.cmd in ["issues", "quotes", "polls", "tumblrs"]:
handle_show_pending(args)
elif args.cmd == "pending":
if args.args:
args.send("Invalid argument %s." % args.args[0])
else:
admins = ": ".join(args.handler.admins)
show_pending(args.db, admins, args.send)
elif args.cmd == "enabled":
if not args.args:
args.send("Missing argument.")
elif args.args[0] == "commands":
mods = ", ".join(sorted(command.registry.get_enabled_commands()))
args.send(mods, ignore_length=True)
elif args.args[0] == "hooks":
mods = ", ".join(sorted(hook.registry.get_enabled_hooks()))
args.send(mods)
else:
args.send("Invalid argument.")
elif args.cmd == "disabled":
if not args.args:
args.send("Missing argument.")
elif args.args[0] == "commands":
mods = ", ".join(sorted(command.registry.get_disabled_commands()))
args.send(mods if mods else "No disabled commands.")
elif args.args[0] == "hooks":
mods = ", ".join(sorted(hook.registry.get_disabled_hooks()))
args.send(mods if mods else "No disabled hooks.")
else:
args.send("Invalid argument.")
[docs]def show_pending_items(type, items, send):
for x in items:
if type == 'quotes':
send("#%d %s -- %s, Submitted by %s" % (x.id, x.quote, x.nick, x.submitter))
elif type == 'issues':
nick = x.source.split('!')[0]
send("#%d %s, Submitted by %s" % (x.id, x.title, nick))
elif type == 'polls':
send("#%d -- %s, Submitted by %s" % (x.id, x.question, x.submitter))
elif type == 'tumblrs':
send("#%d -- %s for %s, Submitted by %s" % (x.id, x.post, x.blogname, x.submitter))
[docs]def show_pending(db, admins, send, ping=False):
pending = {'issues': [], 'quotes': [], 'polls': [], 'tumblrs': []}
for name in pending:
table = getattr(orm, name.capitalize())
pending[name] = db.query(table).filter(table.accepted == 0).all()
if any(pending.values()):
if ping:
send("%s: Items are Pending Approval" % admins)
elif not ping:
send("No items are Pending")
for type, items in pending.items():
if items:
send("%s:" % type.capitalize())
show_pending_items(type, items, send)
[docs]def handle_accept(args):
table = getattr(orm, args.cmd.capitalize() + "s")
pending = args.db.query(table).filter(table.accepted == 0, table.id == args.num).first()
if pending is None:
args.send("Not a valid %s" % args.cmd)
return
msg, success = get_accept_msg(args.handler, pending, args.cmd)
if not success:
args.send(msg)
return
pending.accepted = 1
ctrlchan = args.handler.config['core']['ctrlchan']
channel = args.handler.config['core']['channel']
botnick = args.handler.config['core']['nick']
nick = pending.source.split('!')[0] if args.cmd == 'issue' else pending.submitter
args.handler.connection.privmsg_many([ctrlchan, channel, nick], msg)
args.handler.do_log('private', botnick, msg, 'privmsg')
[docs]def get_accept_msg(handler, pending, type):
success = True
if type == 'quote':
msg = "Quote #%d Accepted: %s -- %s, Submitted by %s" % (pending.id, pending.quote, pending.nick, pending.submitter)
elif type == 'poll':
msg = "Poll #%d accepted: %s, Submitted by %s" % (pending.id, pending.question, pending.submitter)
elif type == 'issue':
repo = handler.config['api']['githubrepo']
apikey = handler.config['api']['githubapikey']
msg, success = web.create_issue(pending.title, pending.description, pending.source, repo, apikey)
if success:
msg = "Issue Created -- %s -- %s" % (msg, pending.title)
elif type == 'tumblr':
msg, success = web.post_tumblr(handler.config, pending.blog, pending.post)
if success:
msg = "Tumblr post #%d accepted: %s, Submitted by %s" % (pending.id, pending.post, pending.submitter)
return msg, success
[docs]def handle_reject(args):
table = getattr(orm, args.cmd.capitalize() + "s")
pending = args.db.query(table).get(args.num)
if pending is None:
args.send("Not a valid %s" % args.cmd)
return
if pending.accepted == 1:
args.send("%s already accepted" % args.cmd.capitialize())
return
ctrlchan = args.handler.config['core']['ctrlchan']
channel = args.handler.config['core']['channel']
botnick = args.handler.config['core']['nick']
msg = get_reject_msg(pending, args.cmd)
nick = pending.source.split('!')[0] if args.cmd == 'issue' else pending.submitter
args.handler.connection.privmsg_many([ctrlchan, channel, nick], msg)
args.handler.do_log('private', botnick, msg, 'privmsg')
args.db.delete(pending)
[docs]def get_reject_msg(pending, type):
if type == 'issue':
nick = pending.source.split('!')[0]
return "Issue Rejected -- %s, Submitted by %s" % (pending.title, nick)
elif type == 'quote':
return "Quote #%d Rejected: %s -- %s, Submitted by %s" % (pending.id, pending.quote, pending.nick, pending.submitter)
elif type == 'poll':
return "Poll #%d rejected: %s, Submitted by %s" % (pending.id, pending.question, pending.submitter)
elif type == 'tumblr':
return "Tumblr #%d rejected: %s, Submitted by %s" % (pending.id, pending.post, pending.submitter)
[docs]def handle_quote(args):
if args.cmd[0] == "join":
args.send("quote join is not suported, use !join.")
else:
args.handler.connection.send_raw(" ".join(args.cmd))
[docs]def handle_help(args):
args.send("quote <raw command>")
args.send("cs|chanserv <chanserv command>")
args.send("disable|enable <kick|command <command>|hook <hook>|all <commands|hooks>|logging|chanlog>")
args.send("show <guarded|issues|quotes|polls|pending|tumblr> <disabled|enabled> <commands|hooks>")
args.send("accept|reject <issue|quote|poll> <num>")
args.send("guard|unguard <nick>")
[docs]def init_parser(send, handler, db):
parser = arguments.ArgParser(handler.config)
parser.set_defaults(send=send, handler=handler, db=db)
subparser = parser.add_subparsers()
quote_parser = subparser.add_parser('quote')
quote_parser.add_argument('cmd', nargs='+')
quote_parser.set_defaults(func=handle_quote)
help_parser = subparser.add_parser('help')
help_parser.set_defaults(func=handle_help)
cs_parser = subparser.add_parser('chanserv', aliases=['cs'])
cs_parser.add_argument('cmd', choices=['op', 'deop', 'voice', 'devoice'])
cs_parser.add_argument('args', nargs='+')
cs_parser.set_defaults(func=handle_chanserv)
disable_parser = subparser.add_parser('disable')
disable_parser.add_argument('cmd', choices=['kick', 'command', 'hook', 'logging', 'chanlog'])
disable_parser.add_argument('args', nargs='*')
disable_parser.set_defaults(func=handle_disable)
enable_parser = subparser.add_parser('enable')
enable_parser.add_argument('cmd', choices=['kick', 'command', 'hook', 'logging', 'chanlog', 'all'])
enable_parser.add_argument('args', nargs='*')
enable_parser.set_defaults(func=handle_enable)
guard_parser = subparser.add_parser('guard')
guard_parser.add_argument('nick', action=arguments.NickParser)
guard_parser.set_defaults(func=handle_guard)
unguard_parser = subparser.add_parser('unguard')
unguard_parser.add_argument('nick', action=arguments.NickParser)
unguard_parser.set_defaults(func=handle_unguard)
# We need the config in the guard_parser and unguard_parser namespaces but there's no way to pass arbitrary
# arguments to subparser.add_parser, and now way to talk to the parent parser from subparsers. Thus, we must
# fall back on hacky crap like this
guard_parser.namespace.config = handler.config
unguard_parser.namespace.config = handler.config
show_parser = subparser.add_parser('show')
show_parser.add_argument('cmd', choices=['guarded', 'issues', 'quotes', 'polls', 'pending', 'disabled', 'enabled'])
show_parser.add_argument('args', nargs='*')
show_parser.set_defaults(func=handle_show)
show_parser = subparser.add_parser('accept')
show_parser.add_argument('cmd', choices=['issue', 'quote', 'poll', 'tumblr'])
show_parser.add_argument('num', type=int)
show_parser.set_defaults(func=handle_accept)
show_parser = subparser.add_parser('reject')
show_parser.add_argument('cmd', choices=['issue', 'quote', 'poll', 'tumblr'])
show_parser.add_argument('num', type=int)
show_parser.set_defaults(func=handle_reject)
return parser
[docs]def handle_ctrlchan(handler, msg, send):
""" Handle the control channel."""
with handler.db.session_scope() as db:
parser = init_parser(send, handler, db)
try:
cmdargs = parser.parse_args(msg)
except arguments.ArgumentException as e:
# FIXME: figure out a better way to allow non-commands without spamming the channel.
err_str = r"invalid choice: .* \(choose from 'quote', 'help', 'chanserv', 'cs', 'disable', 'enable', 'guard', 'unguard', 'show', 'accept', 'reject'\)"
if not re.match(err_str, str(e)):
send(str(e))
return
cmdargs.func(cmdargs)