diff --git a/botconfig.py.example b/botconfig.py.example index 8102987..f289399 100644 --- a/botconfig.py.example +++ b/botconfig.py.example @@ -22,11 +22,7 @@ CMD_CHAR = "!" SERVER_PASS = "{account}:{password}" OWNERS = ("unaffiliated/wolfbot_admin1",) # The comma is required at the end if there is only one owner. -ADMINS = ("unaffiliated/wolfbot_admin2", "unaffiliated/wolfbot_test*") - OWNERS_ACCOUNTS = ("1owner_acc",) -ADMINS_ACCOUNTS = ("1admin_acc", "2admin_acc") - ALLOWED_NORMAL_MODE_COMMANDS = [] # Debug mode commands to be allowed in normal mode OWNERS_ONLY_COMMANDS = [] # Commands that should only be allowed for owners, regardless of their original permissions diff --git a/messages/en.json b/messages/en.json index 1f0060e..1220193 100644 --- a/messages/en.json +++ b/messages/en.json @@ -846,6 +846,19 @@ "fwarn_view_deny": "denied {0}.", "fwarn_reason_required": "A public warning reason is required.", "warn_unacked": "You have unacknowledged warnings and cannot join at this time. Use \"warn list\" to view them.", + "no_templates": "There are no access templates defined.", + "template_not_found": "There is no template named {0}.", + "template_set": "Set template {0} to flags +{1}.", + "template_deleted": "Removed template {0}. Any access entries using this template have also been deleted.", + "access_set_account": "Set access for account {0} to +{1}.", + "access_set_host": "Set access for host {0} to +{1}.", + "access_deleted_account": "Deleted access for account {0}.", + "access_deleted_host": "Deleted access for host {0}.", + "invalid_flag": "Invalid flag {0}. Valid flags are +{1}.", + "no_access_account": "Account {0} does not have any access.", + "access_account": "Account {0} has access +{1}.", + "no_access_host": "Host {0} does not have any access.", + "access_host": "Host {0} has access +{1}.", "_": " vim: set sw=4 expandtab:" } diff --git a/src/db.py b/src/db.py index eae9dd5..9b1f208 100644 --- a/src/db.py +++ b/src/db.py @@ -168,6 +168,56 @@ def expire_stasis(): stasis_expires IS NOT NULL AND stasis_expires <= datetime('now')""") +def get_template(name): + c = conn.cursor() + c.execute("SELECT id, flags FROM access_template WHERE name = ?", (name,)) + row = c.fetchone() + if row is None: + return (None, set()) + return (row[0], row[1]) + +def get_templates(): + c = conn.cursor() + c.execute("SELECT name, flags FROM access_template ORDER BY name ASC") + tpls = [] + for name, flags in c: + tpls.append((name, flags)) + return tpls + +def update_template(name, flags): + with conn: + tid, _ = get_template(name) + c = conn.cursor() + if tid is None: + c.execute("INSERT INTO access_template (name, flags) VALUES (?, ?)", (name, flags)) + else: + c.execute("UPDATE access_template SET flags = ? WHERE id = ?", (flags, tid)) + +def delete_template(name): + with conn: + tid, _ = get_template(name) + if tid is not None: + c = conn.cursor() + c.execute("DELETE FROM access WHERE template = ?", (tid,)) + c.execute("DELETE FROM template WHERE id = ?", (tid,)) + +def set_access(acc, hostmask, flags=None, tid=None): + peid, plid = _get_ids(acc, hostmask) + if peid is None: + return + with conn: + c = conn.cursor() + if flags is None and tid is None: + c.execute("DELETE FROM access WHERE person = ?", (peid,)) + elif tid is not None: + c.execute("""INSERT OR REPLACE INTO access + (person, template, flags) + VALUES (?, ?, NULL)""", (peid, tid)) + else: + c.execute("""INSERT OR REPLACE INTO access + (person, template, flags) + VALUES (?, NULL, ?)""", (peid, flags)) + def toggle_simple(acc, hostmask): _toggle_thing("simple", acc, hostmask) diff --git a/src/settings.py b/src/settings.py index 3376704..a41a28d 100644 --- a/src/settings.py +++ b/src/settings.py @@ -319,6 +319,7 @@ DISABLED_ROLES = frozenset() GIF_CHANCE = 1/50 FORTUNE_CHANCE = 1/25 +ALL_FLAGS = frozenset("AaDdFjms") RULES = (botconfig.CHANNEL + " channel rules: http://wolf.xnrand.com/rules") diff --git a/src/wolfgame.py b/src/wolfgame.py index 3495993..f78a232 100644 --- a/src/wolfgame.py +++ b/src/wolfgame.py @@ -8581,6 +8581,167 @@ def fwarn(cli, nick, chan, rest): else: reply(cli, nick, chan, messages["fwarn_added"].format(warn_id)) +@cmd("ftemplate", "F", pm=True) +def ftemplate(cli, nick, chan, rest): + params = re.split(" +", rest) + + if params[0] == "": + # display a list of all templates + tpls = db.get_templates() + if not tpls: + reply(cli, nick, chan, messages["no_templates"]) + else: + tpls = ["{0} (+{1})".format(name, "".join(sorted(flags))) for name, flags in tpls] + reply(cli, nick, chan, var.break_long_message(tpls, ", ")) + elif len(params) == 1: + reply(cli, nick, chan, messages["not_enough_parameters"]) + else: + name = params[0].upper() + flags = params[1] + tid, cur_flags = db.get_template(name) + + if flags[0] != "+" and flags[0] != "-": + # flags is a template name + tpl_name = flags.upper() + tpl_id, tpl_flags = db.get_template(tpl_name) + if tpl_id is None: + reply(cli, nick, chan, messages["template_not_found"].format(tpl_name)) + return + tpl_flags = "".join(sorted(tpl_flags)) + db.update_template(name, tpl_flags) + reply(cli, nick, chan, messages["template_set"].format(name, tpl_flags)) + else: + adding = True + for flag in flags: + if flag == "+": + adding = True + continue + elif flag == "-": + adding = False + continue + elif flag == "*": + if adding: + cur_flags = cur_flags | (var.ALL_FLAGS - {"F"}) + else: + cur_flags = set() + continue + elif flag not in var.ALL_FLAGS: + reply(cli, nick, chan, messages["invalid_flag"].format(flag, "".join(sorted(var.ALL_FLAGS)))) + return + elif adding: + cur_flags.add(flag) + else: + cur_flags.discard(flag) + if cur_flags: + tpl_flags = "".join(sorted(cur_flags)) + db.update_template(name, tpl_flags) + reply(cli, nick, chan, messages["template_set"].format(name, tpl_flags)) + elif tid is None: + reply(cli, nick, chan, messages["template_not_found"].format(name)) + else: + db.delete_template(name) + reply(cli, nick, chan, messages["template_deleted"].format(name)) + + # re-init var.FLAGS and var.FLAGS_ACCS since they may have changed + db.init_vars() + +@cmd("fflags", flag="F", pm=True) +def fflags(cli, nick, chan, rest): + params = re.split(" +", rest) + + if params[0] == "": + # display a list of all access + parts = [] + for acc, flags in var.FLAGS_ACCS.items(): + if not flags: + continue + if var.ACCOUNTS_ONLY: + parts.append("{0} (+{1})".format(acc, "".join(sorted(flags)))) + else: + parts.append("{0} (Account) (+{1})".format(acc, "".join(sorted(flags)))) + for hm, flags in var.FLAGS.items(): + if not flags: + continue + if var.DISABLE_ACCOUNTS: + parts.append("{0} (+{1})".format(hm, "".join(sorted(flags)))) + else: + parts.append("{0} (Host) (+{1})".format(hm, "".join(sorted(flags)))) + if not parts: + reply(cli, nick, chan, messages["no_access"]) + else: + reply(cli, nick, chan, var.break_long_message(parts, ", ")) + elif len(params) == 1: + # display access for the given user + acc, hm = parse_warning_target(params[0]) + if acc is not None: + if not var.FLAGS_ACCS[acc]: + msg = messages["no_access_account"].format(acc) + else: + msg = messages["access_account"].format(acc, "".join(sorted(var.FLAGS_ACCS[acc]))) + elif hm is not None: + if not var.FLAGS[hm]: + msg = messages["no_access_host"].format(hm) + else: + msg = messages["access_host"].format(acc, "".join(sorted(var.FLAGS[hm]))) + reply(cli, nick, chan, msg) + else: + acc, hm = parse_warning_target(params[0]) + flags = params[1] + cur_flags = set(var.FLAGS_ACCS[acc] + var.FLAGS[hm]) + + if flags[0] != "+" and flags[0] != "-": + # flags is a template name + tpl_name = flags.upper() + tpl_id, tpl_flags = db.get_template(tpl_name) + if tpl_id is None: + reply(cli, nick, chan, messages["template_not_found"].format(tpl_name)) + return + tpl_flags = "".join(sorted(tpl_flags)) + db.set_access(acc, hm, tid=tpl_id) + if acc is not None: + reply(cli, nick, chan, messages["access_set_account"].format(acc, tpl_flags)) + else: + reply(cli, nick, chan, messages["access_set_host"].format(hm, tpl_flags)) + else: + adding = True + for flag in flags: + if flag == "+": + adding = True + continue + elif flag == "-": + adding = False + continue + elif flag == "*": + if adding: + cur_flags = cur_flags | (var.ALL_FLAGS - {"F"}) + else: + cur_flags = set() + continue + elif flag not in var.ALL_FLAGS: + reply(cli, nick, chan, messages["invalid_flag"].format(flag, "".join(sorted(var.ALL_FLAGS)))) + return + elif adding: + cur_flags.add(flag) + else: + cur_flags.discard(flag) + if cur_flags: + flags = "".join(sorted(cur_flags)) + db.set_access(acc, hm, flags=flags) + if acc is not None: + reply(cli, nick, chan, messages["access_set_account"].format(acc, flags)) + else: + reply(cli, nick, chan, messages["access_set_host"].format(hm, flags)) + else: + db.set_access(acc, hm, flags=None) + if acc is not None: + reply(cli, nick, chan, messages["access_deleted_account"].format(acc)) + else: + reply(cli, nick, chan, messages["access_deleted_host"].format(hm)) + + # re-init var.FLAGS and var.FLAGS_ACCS since they may have changed + db.init_vars() + + @cmd("wait", "w", playing=True, phases=("join",)) def wait(cli, nick, chan, rest): """Increases the wait time until !start can be used."""