Finish warning system
- fwarn/warn commands to view and manipulate warnings - fstasis can now only decrease stasis, not add to it - refreshdb command can sync bot game state with what is in the db (including expiring any unexpired stasis or warnings) - stasis now expires - tempban is still not implemented and will not be implemented as part of the PR (it will come later, if ever) - sanctions can be automatically applied after warnings cross a certain threshold; some defaults are configured
This commit is contained in:
parent
bba5ab745e
commit
a6ea55a8fe
@ -574,6 +574,7 @@
|
||||
"account_not_in_stasis": "\u0002{0}\u0002 (Account: {1}) is not in stasis.",
|
||||
"currently_stasised": "Currently stasised: {0}",
|
||||
"noone_stasised": "Nobody is currently stasised.",
|
||||
"stasis_cannot_increase": "Cannot increase stasis using fstasis; use fwarn instead.",
|
||||
"no_command_specified": "Error: No command specified. Did you mean \u0002-cmds\u0002?",
|
||||
"invalid_option": "Invalid option: {0}",
|
||||
"command_does_not_exist": "That command does not exist.",
|
||||
@ -797,14 +798,20 @@
|
||||
"villagergame_nope": "Game over! The villagers decided incorrectly that there are actually no wolves, allowing the wolves to slaughter the remainder of them in their sleep with impunity.",
|
||||
"stop_bot_ingame_safeguard": "Warning: A game is currently running. If you want to {what} the bot anyway, use \"{prefix}{cmd} -force\".",
|
||||
"fwarn_usage": "Usage: fwarn list|view|add|del|set|help. See fwarn help <subcommand> for more details.",
|
||||
"warn_usage": "Usage: warn list|view|ack|help. See warn help <subcommand> for more details.",
|
||||
"fwarn_list_syntax": "Usage: fwarn list [-all] [nick[!user@host]|=account] [page]",
|
||||
"fwarn_view_syntax": "Usage: fwarn view <id>",
|
||||
"fwarn_del_syntax": "Usage: fwarn del <id>",
|
||||
"fwarn_set_syntax": "Usage: fwarn set <id> <reason> [| notes]",
|
||||
"fwarn_help_syntax": "Usage: fwarn help <subcommand>",
|
||||
"warn_list_syntax": "Usage: warn list [-all] [page]",
|
||||
"warn_view_syntax": "Usage: warn view <id>",
|
||||
"warn_ack_syntax": "Usage: warn ack <id>",
|
||||
"warn_help_syntax": "Uwage: warn help <subcommand>",
|
||||
"fwarn_add_syntax": "Usage: fwarn add <nick[!user@host]|=account> [@]<points> [~expiry] [sanctions] <:reason> [| notes]",
|
||||
"fwarn_page_invalid": "Invalid page, must be a number 1 or greater.",
|
||||
"fwarn_points_invalid": "Invalid points, must be a number above 0.",
|
||||
"fwarn_expiry_invalid": "Invalid expiration amount, must be a number above 0.",
|
||||
"fwarn_expiry_invalid_suffix": "Invalid expiration suffix, must use either d, h, or m.",
|
||||
"fwarn_cannot_add": "Cannot add warning, double-check your parameters (the nick might be wrong or you are not joined to the channel).",
|
||||
"fwarn_added": "Added warning {0}.",
|
||||
@ -813,24 +820,32 @@
|
||||
"fwarn_stasis_invalid": "Invalid stasis amount, specify sanction as stasis=number.",
|
||||
"fwarn_deny_invalid": "Invalid denied commands, specify sanction as deny=command,command,command without spaces.",
|
||||
"fwarn_deny_invalid_command": "Invalid command \"{0}\", specify sanction as deny=command,command,command without spaces.",
|
||||
"fwarn_list_header": "{0} has {1} active warning points.",
|
||||
"fwarn_list_header": "{0} has {1} active warning points. Warnings prefixed with \u0002!\u0002 are unacknowledged.",
|
||||
"warn_list_header": "You have {0} active warning points. You must acknowledge all warnings prefixed with \u0002!\u0002 by using \"warn ack <id>\" before you can join games.",
|
||||
"fwarn_list": "{0}{1}[#{2} {3}] to {4} by {5} - {6} ({7} points, {8}){9}",
|
||||
"warn_list": "{0}{1}[#{2} {3}] {4} ({5} points, {6}){7}",
|
||||
"fwarn_deleted": "deleted",
|
||||
"fwarn_expired": "expired",
|
||||
"fwarn_list_expired": "expired on {0}",
|
||||
"fwarn_never_expires": "never expires",
|
||||
"fwarn_list_footer": "More results are available, use fwarn list {0} to view them.",
|
||||
"warn_list_footer": "More results are available, use warn list {0} to view them.",
|
||||
"fwarn_list_empty": "No results.",
|
||||
"fwarn_invalid_warning": "The specified warning id does not exist or you do not have permission to view it.",
|
||||
"fwarn_view_header": "Warning #{0}, given to {1} on {2} by {3}. {4} points. {5}.",
|
||||
"warn_view_header": "Warning #{0}, given on {1}. {2} points. {3}.",
|
||||
"fwarn_view_active": "Currently active, {0}",
|
||||
"fwarn_view_expires": "expires on {0}",
|
||||
"fwarn_view_expired": "Expired on {0}",
|
||||
"fwarn_view_deleted": "Deleted on {0} by {1}",
|
||||
"fwarn_view_ack": "Warning has not yet been acknowledged.",
|
||||
"warn_view_ack": "You have not yet acknowledge this warning. You must acknowledge this warning by using \"warn ack {0}\" before you can join games.",
|
||||
"fwarn_view_sanctions": "Sanctions:",
|
||||
"fwarn_view_stasis": "{0} games of stasis.",
|
||||
"fwarn_view_stasis_sing": "1 game of stasis.",
|
||||
"fwarn_view_stasis_plural": "{0} games of stasis.",
|
||||
"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.",
|
||||
|
||||
"_": " vim: set sw=4 expandtab:"
|
||||
}
|
||||
|
@ -84,10 +84,6 @@ if args.normal: normal = True
|
||||
botconfig.DEBUG_MODE = debug_mode if not normal else False
|
||||
botconfig.VERBOSE_MODE = verbose if not normal else False
|
||||
|
||||
# Initialize Database
|
||||
|
||||
db.init()
|
||||
|
||||
# Logger
|
||||
|
||||
# replace characters that can't be encoded with '?'
|
||||
|
236
src/db.py
236
src/db.py
@ -3,34 +3,37 @@ import src.settings as var
|
||||
import sqlite3
|
||||
import os
|
||||
import json
|
||||
from collections import defaultdict
|
||||
|
||||
# increment this whenever making a schema change so that the schema upgrade functions run on start
|
||||
# they do not run by default for performance reasons
|
||||
SCHEMA_VERSION = 1
|
||||
|
||||
conn = None
|
||||
need_install = not os.path.isfile("data.sqlite3")
|
||||
conn = sqlite3.connect("data.sqlite3")
|
||||
with conn:
|
||||
c = conn.cursor()
|
||||
c.execute("PRAGMA foreign_keys = ON")
|
||||
if need_install:
|
||||
_install()
|
||||
c.execute("PRAGMA user_version")
|
||||
row = c.fetchone()
|
||||
if row[0] == 0:
|
||||
# new schema does not exist yet, migrate from old schema
|
||||
# NOTE: game stats are NOT migrated to the new schema; the old gamestats table
|
||||
# will continue to exist to allow queries against it, however given how horribly
|
||||
# inaccurate the stats on it are, it would be a disservice to copy those inaccurate
|
||||
# statistics over to the new schema which has the capability of actually being accurate.
|
||||
_migrate()
|
||||
elif row[0] < SCHEMA_VERSION:
|
||||
_upgrade()
|
||||
c.close()
|
||||
|
||||
def init():
|
||||
global conn
|
||||
need_install = not os.path.isfile("data.sqlite3")
|
||||
conn = sqlite3.connect("data.sqlite3")
|
||||
with conn:
|
||||
del need_install, c
|
||||
|
||||
def init_vars():
|
||||
with var.GRAVEYARD_LOCK:
|
||||
c = conn.cursor()
|
||||
c.execute("PRAGMA foreign_keys = ON")
|
||||
if need_install:
|
||||
_install()
|
||||
c.execute("PRAGMA user_version")
|
||||
row = c.fetchone()
|
||||
if row[0] == 0:
|
||||
# new schema does not exist yet, migrate from old schema
|
||||
# NOTE: game stats are NOT migrated to the new schema; the old gamestats table
|
||||
# will continue to exist to allow queries against it, however given how horribly
|
||||
# inaccurate the stats on it are, it would be a disservice to copy those inaccurate
|
||||
# statistics over to the new schema which has the capability of actually being accurate.
|
||||
_migrate()
|
||||
elif row[0] < SCHEMA_VERSION:
|
||||
_upgrade()
|
||||
|
||||
c.execute("""SELECT
|
||||
pl.account,
|
||||
pl.hostmask,
|
||||
@ -39,14 +42,37 @@ def init():
|
||||
pe.deadchat,
|
||||
pe.pingif,
|
||||
pe.stasis_amount,
|
||||
pe.stasis_expires
|
||||
pe.stasis_expires,
|
||||
COALESCE(at.flags, a.flags)
|
||||
FROM person pe
|
||||
JOIN person_player pp
|
||||
ON pp.person = pe.id
|
||||
JOIN player pl
|
||||
ON pl.id = pp.player
|
||||
LEFT JOIN access a
|
||||
ON a.person = pe.id
|
||||
LEFT JOIN access_template at
|
||||
ON at.id = a.template
|
||||
WHERE pl.active = 1""")
|
||||
for (acc, host, notice, simple, dc, pi, stasis, stasisexp) in c:
|
||||
|
||||
var.SIMPLE_NOTIFY = set() # cloaks of people who !simple, who don't want detailed instructions
|
||||
var.SIMPLE_NOTIFY_ACCS = set() # same as above, except accounts. takes precedence
|
||||
var.PREFER_NOTICE = set() # cloaks of people who !notice, who want everything /notice'd
|
||||
var.PREFER_NOTICE_ACCS = set() # Same as above, except accounts. takes precedence
|
||||
var.STASISED = defaultdict(int)
|
||||
var.STASISED_ACCS = defaultdict(int)
|
||||
var.PING_IF_PREFS = {}
|
||||
var.PING_IF_PREFS_ACCS = {}
|
||||
var.PING_IF_NUMS = defaultdict(set)
|
||||
var.PING_IF_NUMS_ACCS = defaultdict(set)
|
||||
var.DEADCHAT_PREFS = set()
|
||||
var.DEADCHAT_PREFS_ACCS = set()
|
||||
var.FLAGS = defaultdict(str)
|
||||
var.FLAGS_ACCS = defaultdict(str)
|
||||
var.DENY = defaultdict(set)
|
||||
var.DENY_ACCS = defaultdict(set)
|
||||
|
||||
for acc, host, notice, simple, dc, pi, stasis, stasisexp, flags in c:
|
||||
if acc is not None:
|
||||
if simple == 1:
|
||||
var.SIMPLE_NOTIFY_ACCS.add(acc)
|
||||
@ -59,6 +85,8 @@ def init():
|
||||
var.PING_IF_NUMS_ACCS[pi].add(acc)
|
||||
if dc == 1:
|
||||
var.DEADCHAT_PREFS_ACCS.add(acc)
|
||||
if flags:
|
||||
var.FLAGS_ACCS[acc] = flags
|
||||
elif host is not None:
|
||||
if simple == 1:
|
||||
var.SIMPLE_NOTIFY.add(host)
|
||||
@ -71,6 +99,74 @@ def init():
|
||||
var.PING_IF_NUMS[pi].add(host)
|
||||
if dc == 1:
|
||||
var.DEADCHAT_PREFS.add(host)
|
||||
if flags:
|
||||
var.FLAGS[host] = flags
|
||||
|
||||
c.execute("""SELECT
|
||||
pl.account,
|
||||
pl.hostmask,
|
||||
ws.data
|
||||
FROM warning w
|
||||
JOIN warning_sanction ws
|
||||
ON ws.warning = w.id
|
||||
JOIN person pe
|
||||
ON pe.id = w.target
|
||||
JOIN person_player pp
|
||||
ON pp.person = pe.id
|
||||
JOIN player pl
|
||||
ON pl.id = pp.player
|
||||
WHERE
|
||||
ws.sanction = 'deny command'
|
||||
AND w.deleted = 0
|
||||
AND (
|
||||
w.expires IS NULL
|
||||
OR w.expires > datetime('now')
|
||||
)""")
|
||||
for acc, host, command in c:
|
||||
if acc is not None:
|
||||
var.DENY_ACCS[acc].add(command)
|
||||
if host is not None:
|
||||
var.DENY[host].add(command)
|
||||
|
||||
init_vars()
|
||||
|
||||
def decrement_stasis(acc=None, hostmask=None):
|
||||
peid, plid = _get_ids(acc, hostmask)
|
||||
if (acc is not None or hostmask is not None) and peid is None:
|
||||
return
|
||||
sql = "UPDATE person SET stasis_amount = MAX(0, stasis_amount - 1)"
|
||||
params = ()
|
||||
if peid is not None:
|
||||
sql += " WHERE id = ?"
|
||||
params = (peid,)
|
||||
|
||||
with conn:
|
||||
c = conn.cursor()
|
||||
c.execute(sql, params)
|
||||
|
||||
def decrease_stasis(newamt, acc=None, hostmask=None):
|
||||
peid, plid = _get_ids(acc, hostmask)
|
||||
if peid is None:
|
||||
return
|
||||
if newamt < 0:
|
||||
newamt = 0
|
||||
|
||||
with conn:
|
||||
c = conn.cursor()
|
||||
c.execute("""UPDATE person
|
||||
SET stasis_amount = MIN(stasis_amount, ?)
|
||||
WHERE id = ?""", (newamt, peid))
|
||||
|
||||
def expire_stasis():
|
||||
with conn:
|
||||
c = conn.cursor()
|
||||
c.execute("""UPDATE person
|
||||
SET
|
||||
stasis_amount = 0,
|
||||
stasis_expires = NULL
|
||||
WHERE
|
||||
stasis_expires IS NOT NULL
|
||||
AND stasis_expires <= datetime('now')""")
|
||||
|
||||
def toggle_simple(acc, hostmask):
|
||||
_toggle_thing("simple", acc, hostmask)
|
||||
@ -84,9 +180,6 @@ def toggle_deadchat(acc, hostmask):
|
||||
def set_pingif(val, acc, hostmask):
|
||||
_set_thing("pingif", val, acc, hostmask, raw=False)
|
||||
|
||||
def set_stasis(val, acc, hostmask):
|
||||
_set_thing("stasis_amount", val, acc, hostmask, raw=False)
|
||||
|
||||
def add_game(mode, size, started, finished, winner, players, options):
|
||||
""" Adds a game record to the database.
|
||||
|
||||
@ -257,39 +350,6 @@ def get_game_totals(mode):
|
||||
totals.append("\u0002{0}p\u0002: {1}".format(*row))
|
||||
return "Total games ({0}) | {1}".format(total_games, ", ".join(totals))
|
||||
|
||||
def get_flags(acc, hostmask):
|
||||
peid, plid = _get_ids(acc, hostmask)
|
||||
c = conn.cursor()
|
||||
c.execute("""SELECT COALESCE(at.flags, a.flags)
|
||||
FROM access a
|
||||
LEFT JOIN access_template at
|
||||
ON at.id = a.template
|
||||
WHERE a.person = ?""", (peid,))
|
||||
row = c.fetchone()
|
||||
if row is None:
|
||||
return ""
|
||||
return row[0]
|
||||
|
||||
def get_denied_commands(acc, hostmask):
|
||||
peid, plid = _get_ids(acc, hostmask)
|
||||
c = conn.cursor()
|
||||
c.execute("""SELECT ws.data
|
||||
FROM warning w
|
||||
JOIN warning_sanction ws
|
||||
ON ws.warning = w.id
|
||||
WHERE
|
||||
ws.sanction = 'deny command'
|
||||
AND w.target = ?
|
||||
AND w.deleted = 0
|
||||
AND (
|
||||
w.expires IS NULL
|
||||
OR w.expires > datetime('now')
|
||||
)""", (peid,))
|
||||
cmds = set()
|
||||
for row in c:
|
||||
cmds.add(row[0])
|
||||
return cmds
|
||||
|
||||
def get_warning_points(acc, hostmask):
|
||||
peid, plid = _get_ids(acc, hostmask)
|
||||
c = conn.cursor()
|
||||
@ -305,6 +365,23 @@ def get_warning_points(acc, hostmask):
|
||||
row = c.fetchone()
|
||||
return row[0]
|
||||
|
||||
def has_unacknowledged_warnings(acc, hostmask):
|
||||
peid, plid = _get_ids(acc, hostmask)
|
||||
if peid is None:
|
||||
return False
|
||||
c = conn.cursor()
|
||||
c.execute("""SELECT MIN(acknowledged)
|
||||
FROM warning
|
||||
WHERE
|
||||
target = ?
|
||||
AND deleted = 0
|
||||
AND (
|
||||
expires IS NULL
|
||||
OR expires > datetime('now')
|
||||
)""", (peid,))
|
||||
row = c.fetchone()
|
||||
return not bool(row[0])
|
||||
|
||||
def list_all_warnings(list_all=False, skip=0, show=0):
|
||||
c = conn.cursor()
|
||||
sql = """SELECT
|
||||
@ -356,7 +433,7 @@ def list_all_warnings(list_all=False, skip=0, show=0):
|
||||
"reason": row[9]})
|
||||
return warnings
|
||||
|
||||
def list_warnings(acc, hostmask, list_all=False, skip=0, show=0):
|
||||
def list_warnings(acc, hostmask, expired=False, deleted=False, skip=0, show=0):
|
||||
peid, plid = _get_ids(acc, hostmask)
|
||||
c = conn.cursor()
|
||||
sql = """SELECT
|
||||
@ -383,16 +460,16 @@ def list_warnings(acc, hostmask, list_all=False, skip=0, show=0):
|
||||
WHERE
|
||||
warning.target = ?
|
||||
"""
|
||||
if not list_all:
|
||||
sql += """ AND deleted = 0
|
||||
AND (
|
||||
if not deleted:
|
||||
sql += " AND deleted = 0"
|
||||
if not expired:
|
||||
sql += """ AND (
|
||||
expires IS NULL
|
||||
OR expires > datetime('now')
|
||||
)
|
||||
"""
|
||||
sql += "ORDER BY warning.issued DESC\n"
|
||||
)"""
|
||||
sql += " ORDER BY warning.issued DESC"
|
||||
if show > 0:
|
||||
sql += "LIMIT {0} OFFSET {1}".format(show, skip)
|
||||
sql += " LIMIT {0} OFFSET {1}".format(show, skip)
|
||||
|
||||
c.execute(sql, (botconfig.NICK, peid))
|
||||
warnings = []
|
||||
@ -410,7 +487,7 @@ def list_warnings(acc, hostmask, list_all=False, skip=0, show=0):
|
||||
return warnings
|
||||
|
||||
def get_warning(warn_id, acc=None, hm=None):
|
||||
pe, pl = _get_ids(acc, hm)
|
||||
peid, plid = _get_ids(acc, hm)
|
||||
c = conn.cursor()
|
||||
sql = """SELECT
|
||||
warning.id,
|
||||
@ -486,13 +563,14 @@ def get_warning_sanctions(warn_id):
|
||||
def add_warning(tacc, thm, sacc, shm, amount, reason, notes, expires, need_ack):
|
||||
teid, tlid = _get_ids(tacc, thm)
|
||||
seid, slid = _get_ids(sacc, shm)
|
||||
ack = 0 if need_ack else 1
|
||||
with conn:
|
||||
c = conn.cursor()
|
||||
c.execute("""INSERT INTO warning
|
||||
(
|
||||
target, sender, amount,
|
||||
issued, expires,
|
||||
reasons, notes,
|
||||
reason, notes,
|
||||
acknowledged
|
||||
)
|
||||
VALUES
|
||||
@ -501,7 +579,7 @@ def add_warning(tacc, thm, sacc, shm, amount, reason, notes, expires, need_ack):
|
||||
datetime('now'), ?,
|
||||
?, ?,
|
||||
?
|
||||
)""", (teid, seid, amount, expires, reasons, notes, not need_ack))
|
||||
)""", (teid, seid, amount, expires, reason, notes, ack))
|
||||
return c.lastrowid
|
||||
|
||||
def add_warning_sanction(warning, sanction, data):
|
||||
@ -512,6 +590,19 @@ def add_warning_sanction(warning, sanction, data):
|
||||
VALUES
|
||||
(?, ?, ?)""", (warning, sanction, data))
|
||||
|
||||
if sanction == "stasis":
|
||||
c.execute("SELECT target FROM warning WHERE id = ?", (warning,))
|
||||
peid = c.fetchone()[0]
|
||||
c.execute("""UPDATE person
|
||||
SET
|
||||
stasis_amount = stasis_amount + ?,
|
||||
stasis_expires = datetime(CASE WHEN stasis_expires IS NULL
|
||||
OR stasis_expires <= datetime('now')
|
||||
THEN 'now'
|
||||
ELSE stasis_expires END,
|
||||
'+{0} hours')
|
||||
WHERE id = ?""".format(int(data)), (data, peid))
|
||||
|
||||
def del_warning(warning, acc, hm):
|
||||
peid, plid = _get_ids(acc, hm)
|
||||
with conn:
|
||||
@ -533,6 +624,11 @@ def set_warning(warning, reason, notes):
|
||||
SET reason = ?, notes = ?
|
||||
WHERE id = ?""", (reason, notes, warning))
|
||||
|
||||
def acknowledge_warning(warning):
|
||||
with conn:
|
||||
c = conn.cursor()
|
||||
c.execute("UPDATE warning SET acknowledged = 1 WHERE id = ?", (warning,))
|
||||
|
||||
def _upgrade():
|
||||
# no upgrades yet, once there are some, add methods like _add_table(), _add_column(), etc.
|
||||
# that check for the existence of that table/column/whatever and adds/drops/whatevers them
|
||||
|
@ -188,15 +188,13 @@ class cmd:
|
||||
cli.notice(nick, messages["not_owner"])
|
||||
return
|
||||
|
||||
# TODO: cache flags and cmds (below) on init, possibly store in var.USERS
|
||||
# that would greatly reduce our db calls
|
||||
flags = db.get_flags(acc, hostmask)
|
||||
flags = var.FLAGS[hostmask] + var.FLAGS_ACCS[acc]
|
||||
is_full_admin = "F" in flags
|
||||
if self.flag and (is_full_admin or is_owner):
|
||||
adminlog(chan, rawnick, self.name, rest)
|
||||
return self.func(*largs)
|
||||
|
||||
denied_cmds = db.get_denied_commands(acc, hostmask)
|
||||
denied_cmds = var.DENY[hostmask] | var.DENY_ACCS[acc]
|
||||
for command in self.cmds:
|
||||
if command in denied_cmds:
|
||||
if chan == nick:
|
||||
|
@ -1,5 +1,6 @@
|
||||
import fnmatch
|
||||
import re
|
||||
import threading
|
||||
from collections import defaultdict, OrderedDict
|
||||
|
||||
import botconfig
|
||||
@ -195,10 +196,6 @@ TOTEM_CHANCES = { "death": ( 1 , 1 , 0
|
||||
|
||||
GAME_MODES = {}
|
||||
GAME_PHASES = ("night", "day") # all phases that constitute "in game", game modes can extend this with custom phases
|
||||
SIMPLE_NOTIFY = set() # cloaks of people who !simple, who don't want detailed instructions
|
||||
SIMPLE_NOTIFY_ACCS = set() # same as above, except accounts. takes precedence
|
||||
PREFER_NOTICE = set() # cloaks of people who !notice, who want everything /notice'd
|
||||
PREFER_NOTICE_ACCS = set() # Same as above, except accounts. takes precedence
|
||||
|
||||
ACCOUNTS_ONLY = False # If True, will use only accounts for everything
|
||||
DISABLE_ACCOUNTS = False # If True, all account-related features are disabled. Automatically set if we discover we do not have proper ircd support for accounts
|
||||
@ -212,9 +209,6 @@ NICKSERV_REGAIN_COMMAND = "REGAIN {nick}"
|
||||
CHANSERV = "ChanServ"
|
||||
CHANSERV_OP_COMMAND = "OP {channel}"
|
||||
|
||||
STASISED = defaultdict(int)
|
||||
STASISED_ACCS = defaultdict(int)
|
||||
|
||||
# TODO: move this to a game mode called "fixed" once we implement a way to randomize roles (and have that game mode be called "random")
|
||||
DEFAULT_ROLE = "villager"
|
||||
ROLE_INDEX = ( 4 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 15 , 16 , 18 , 20 , 21 , 23 , 24 )
|
||||
@ -328,16 +322,9 @@ FORTUNE_CHANCE = 1/25
|
||||
|
||||
RULES = (botconfig.CHANNEL + " channel rules: http://wolf.xnrand.com/rules")
|
||||
|
||||
# pingif-related mappings
|
||||
|
||||
PING_IF_PREFS = {}
|
||||
PING_IF_PREFS_ACCS = {}
|
||||
|
||||
PING_IF_NUMS = defaultdict(set)
|
||||
PING_IF_NUMS_ACCS = defaultdict(set)
|
||||
|
||||
DEADCHAT_PREFS = set()
|
||||
DEADCHAT_PREFS_ACCS = set()
|
||||
GRAVEYARD_LOCK = threading.RLock()
|
||||
WARNING_LOCK = threading.RLock()
|
||||
WAIT_TB_LOCK = threading.RLock()
|
||||
|
||||
#TODO: move all of these to util.py or other files, as they are certainly NOT settings!
|
||||
|
||||
@ -386,7 +373,7 @@ def is_admin(nick, ident=None, host=None, acc=None):
|
||||
if not acc:
|
||||
acc = USERS[nick]["account"]
|
||||
hostmask = nick + "!" + ident + "@" + host
|
||||
flags = db.get_flags(acc, hostmask)
|
||||
flags = var.FLAGS[hostmask] + var.FLAGS_ACCS[acc]
|
||||
return "F" in flags
|
||||
|
||||
def irc_lower(nick):
|
||||
|
342
src/wolfgame.py
342
src/wolfgame.py
@ -89,9 +89,6 @@ var.LAST_SAID_TIME = {}
|
||||
|
||||
var.GAME_START_TIME = datetime.now() # for idle checker only
|
||||
var.CAN_START_TIME = 0
|
||||
var.GRAVEYARD_LOCK = threading.RLock()
|
||||
var.WARNING_LOCK = threading.RLock()
|
||||
var.WAIT_TB_LOCK = threading.RLock()
|
||||
var.STARTED_DAY_PLAYERS = 0
|
||||
|
||||
var.DISCONNECTED = {} # players who got disconnected
|
||||
@ -361,6 +358,15 @@ def get_victim(cli, nick, victim, in_chan, self_in_list=False, bot_in_list=False
|
||||
return
|
||||
return pl[pll.index(tempvictim)] #convert back to normal casing
|
||||
|
||||
# wrapper around complete_match() used for any nick on the channel
|
||||
def get_nick(cli, nick):
|
||||
ul = [x for x in var.USERS]
|
||||
ull = [x.lower() for x in var.USERS]
|
||||
lnick, num_matches = complete_match(nick.lower(), ull)
|
||||
if not lnick:
|
||||
return None
|
||||
return ul[ull.index(lnick)]
|
||||
|
||||
def get_roles(*roles):
|
||||
all_roles = []
|
||||
for role in roles:
|
||||
@ -449,6 +455,10 @@ def sync_modes(cli):
|
||||
|
||||
mass_mode(cli, voices, other)
|
||||
|
||||
@cmd("refreshdb", flag="m", pm=True)
|
||||
def refreshdb(cli, nick, chan, rest):
|
||||
"""Updates our tracking vars to the current db state."""
|
||||
db.init_vars()
|
||||
|
||||
@cmd("fdie", "fbye", flag="D", pm=True)
|
||||
def forced_exit(cli, nick, chan, rest):
|
||||
@ -1162,11 +1172,13 @@ def join_player(cli, player, chan, who=None, forced=False, *, sanity=True):
|
||||
ident = var.USERS[player]["ident"]
|
||||
host = var.USERS[player]["host"]
|
||||
acc = var.USERS[player]["account"]
|
||||
hostmask = player + "!" + ident + "@" + host
|
||||
elif is_fake_nick(player) and botconfig.DEBUG_MODE:
|
||||
# fakenick
|
||||
ident = None
|
||||
host = None
|
||||
acc = None
|
||||
hostmask = None
|
||||
else:
|
||||
return False # Not normal
|
||||
if not acc or acc == "*" or var.DISABLE_ACCOUNTS:
|
||||
@ -1183,6 +1195,11 @@ def join_player(cli, player, chan, who=None, forced=False, *, sanity=True):
|
||||
"s" if stasis != 1 else ""))
|
||||
return False
|
||||
|
||||
# don't check unacked warnings on fjoin
|
||||
if who == player and db.has_unacknowledged_warnings(acc, hostmask):
|
||||
cli.notice(player, messages["warn_unacked"])
|
||||
return False
|
||||
|
||||
cmodes = [("+v", player)]
|
||||
if var.PHASE == "none":
|
||||
if var.AUTO_TOGGLE_MODES and player in var.USERS and var.USERS[player]["modes"]:
|
||||
@ -1297,6 +1314,9 @@ def kill_join(cli, chan):
|
||||
reset()
|
||||
cli.msg(chan, msg)
|
||||
cli.msg(chan, messages["game_idle_cancel"])
|
||||
# use this opportunity to expire pending stasis
|
||||
db.expire_stasis()
|
||||
db.init_vars()
|
||||
if var.AFTER_FLASTGAME is not None:
|
||||
var.AFTER_FLASTGAME()
|
||||
var.AFTER_FLASTGAME = None
|
||||
@ -7907,19 +7927,15 @@ def decrement_stasis(nick=None):
|
||||
acc = var.USERS[nick]["account"]
|
||||
# decrement account stasis even if accounts are disabled
|
||||
if acc in var.STASISED_ACCS:
|
||||
var.STASISED_ACCS[acc] = max(0, var.STASISED_ACCS[acc] - 1)
|
||||
db.set_stasis(var.STASISED_ACCS[acc], acc, None)
|
||||
db.decrement_stasis(acc=acc)
|
||||
for hostmask in var.STASISED:
|
||||
if var.match_hostmask(hostmask, nick, ident, host):
|
||||
var.STASISED[hostmask] = max(0, var.STASISED[hostmask] - 1)
|
||||
db.set_stasis(var.STASISED[hostmask], None, hostmask)
|
||||
db.decrement_stasis(hostmask=hostmask)
|
||||
else:
|
||||
for acc in var.STASISED_ACCS:
|
||||
var.STASISED_ACCS[acc] = max(0, var.STASISED_ACCS[acc] - 1)
|
||||
db.set_stasis(var.STASISED_ACCS[acc], acc, None)
|
||||
for hostmask in var.STASISED:
|
||||
var.STASISED[hostmask] = max(0, var.STASISED[hostmask] - 1)
|
||||
db.set_stasis(var.STASISED[hostmask], None, hostmask)
|
||||
db.decrement_stasis()
|
||||
# Also expire any expired stasis and update our tracking vars
|
||||
db.expire_stasis()
|
||||
db.init_vars()
|
||||
|
||||
def parse_warning_target(target):
|
||||
if target[0] == "=":
|
||||
@ -7988,6 +8004,9 @@ def add_warning(target, amount, actor, reason, notes=None, expires=None, need_ac
|
||||
for cmd in sanctions["deny"]:
|
||||
db.add_warning_sanction(sid, "deny command", cmd)
|
||||
|
||||
# Update any tracking vars that may have changed due to this
|
||||
db.init_vars()
|
||||
|
||||
return sid
|
||||
|
||||
@cmd("stasis", chan=True, pm=True)
|
||||
@ -8000,14 +8019,226 @@ def stasis(cli, nick, chan, rest):
|
||||
|
||||
reply(cli, nick, chan, msg, prefix_nick=True)
|
||||
|
||||
@cmd("fstasis", flag="A", chan=True, pm=True)
|
||||
def fstasis(cli, nick, chan, rest):
|
||||
"""Removes or views stasis penalties."""
|
||||
|
||||
data = rest.split()
|
||||
msg = None
|
||||
|
||||
if data:
|
||||
lusers = {k.lower(): v for k, v in var.USERS.items()}
|
||||
acc, hostmask = parse_warning_target(data[0])
|
||||
cur = max(var.STASISED[hostmask], var.STASISED_ACCS[acc])
|
||||
|
||||
if len(data) == 1:
|
||||
if acc is not None and var.STASISED_ACCS[acc] == cur:
|
||||
plural = "" if cur == 1 else "s"
|
||||
reply(cli, nick, chan, messages["account_in_stasis"].format(data[0], acc, cur, plural))
|
||||
elif hostmask is not None and var.STASISED[hostmask] == cur:
|
||||
plural = "" if cur == 1 else "s"
|
||||
reply(cli, nick, chan, messages["hostmask_in_stasis"].format(data[0], hostmask, cur, plural))
|
||||
elif acc is not None:
|
||||
reply(cli, nick, chan, messages["account_not_in_stasis"].format(data[0], acc))
|
||||
else:
|
||||
reply(cli, nick, chan, messages["hostmask_not_in_stasis"].format(data[0], hostmask))
|
||||
else:
|
||||
try:
|
||||
amt = int(data[1])
|
||||
except ValueError:
|
||||
reply(cli, nick, chan, messages["stasis_not_negative"])
|
||||
return
|
||||
|
||||
if amt < 0:
|
||||
reply(cli, nick, chan, messages["stasis_not_negative"])
|
||||
return
|
||||
elif amt > cur:
|
||||
reply(cli, nick, chan, messages["stasis_cannot_increase"])
|
||||
return
|
||||
elif cur == 0:
|
||||
if acc is not None:
|
||||
reply(cli, nick, chan, messages["account_not_in_stasis"].format(data[0], acc))
|
||||
return
|
||||
else:
|
||||
reply(cli, nick, chan, messages["hostmask_not_in_stasis"].format(data[0], hostmask))
|
||||
return
|
||||
|
||||
db.decrease_stasis(amt, acc, hostmask)
|
||||
db.init_vars()
|
||||
if amt > 0:
|
||||
plural = "" if amt == 1 else "s"
|
||||
if acc is not None:
|
||||
reply(cli, nick, chan, messages["fstasis_account_add"].format(data[0], acc, amt, plural))
|
||||
else:
|
||||
reply(cli, nick, chan, messages["fstasis_hostmask_add"].format(data[0], hostmask, amt, plural))
|
||||
elif acc is not None:
|
||||
reply(cli, nick, chan, messages["fstasis_account_remove"].format(data[0], acc))
|
||||
else:
|
||||
reply(cli, nick, chan, messages["fstasis_hostmask_remove"].format(data[0], hostmask))
|
||||
elif var.STASISED or var.STASISED_ACCS:
|
||||
stasised = {}
|
||||
for hostmask in var.STASISED:
|
||||
if var.DISABLE_ACCOUNTS:
|
||||
stasised[hostmask] = var.STASISED[hostmask]
|
||||
else:
|
||||
stasised[hostmask+" (Host)"] = var.STASISED[hostmask]
|
||||
if not var.DISABLE_ACCOUNTS:
|
||||
for acc in var.STASISED_ACCS:
|
||||
stasised[acc+" (Account)"] = var.STASISED_ACCS[acc]
|
||||
msg = messages["currently_stasised"].format(", ".join(
|
||||
"\u0002{0}\u0002 ({1})".format(usr, number)
|
||||
for usr, number in stasised.items()))
|
||||
reply(cli, nick, chan, msg)
|
||||
else:
|
||||
reply(cli, nick, chan, messages["noone_stasised"])
|
||||
|
||||
@cmd("warn", pm=True)
|
||||
def warn(cli, nick, chan, rest):
|
||||
"""View and acknowledge your warnings."""
|
||||
# !warn list [all] - lists all active warnings, or all warnings if all passed
|
||||
# !warn list [-all] [page] - lists all active warnings, or all warnings if all passed
|
||||
# !warn view <id> - views details on warning id
|
||||
# !warn ack <id> - acknowledges warning id
|
||||
# Default if only !warn is given is to do !warn list.
|
||||
pass
|
||||
params = re.split(" +", rest)
|
||||
|
||||
try:
|
||||
command = params.pop(0)
|
||||
if command == "":
|
||||
command = "list"
|
||||
except IndexError:
|
||||
command = "list"
|
||||
|
||||
if command not in ("list", "view", "ack", "help"):
|
||||
reply(cli, nick, chan, messages["warn_usage"])
|
||||
return
|
||||
|
||||
if command == "help":
|
||||
try:
|
||||
subcommand = params.pop(0)
|
||||
except IndexError:
|
||||
reply(cli, nick, chan, messages["warn_help_syntax"])
|
||||
return
|
||||
if subcommand not in ("list", "view", "ack", "help"):
|
||||
reply(cli, nick, chan, messages["warn_usage"])
|
||||
return
|
||||
reply(cli, nick, chan, messages["warn_{0}_syntax".format(subcommand)])
|
||||
return
|
||||
|
||||
if command == "list":
|
||||
list_all = False
|
||||
page = 1
|
||||
try:
|
||||
list_all = params.pop(0)
|
||||
target = params.pop(0)
|
||||
page = int(params.pop(0))
|
||||
except IndexError:
|
||||
pass
|
||||
except ValueError:
|
||||
reply(cli, nick, chan, messages["fwarn_page_invalid"])
|
||||
return
|
||||
|
||||
try:
|
||||
if list_all and list_all != "-all":
|
||||
page = int(list_all)
|
||||
list_all = False
|
||||
elif list_all == "-all":
|
||||
list_all = True
|
||||
except ValueError:
|
||||
reply(cli, nick, chan, messages["fwarn_page_invalid"])
|
||||
return
|
||||
|
||||
acc, hm = parse_warning_target(nick)
|
||||
warnings = db.list_warnings(acc, hm, expired=list_all, skip=(page-1)*10, show=11)
|
||||
points = db.get_warning_points(acc, hm)
|
||||
cli.notice(nick, messages["warn_list_header"].format(points))
|
||||
|
||||
i = 0
|
||||
for warn in warnings:
|
||||
i += 1
|
||||
if (i == 11):
|
||||
parts = []
|
||||
if list_all:
|
||||
parts.append("-all")
|
||||
parts.append(str(page + 1))
|
||||
cli.notice(nick, messages["warn_list_footer"].format(" ".join(parts)))
|
||||
break
|
||||
start = ""
|
||||
end = ""
|
||||
ack = ""
|
||||
if warn["expires"] is not None:
|
||||
if warn["expired"]:
|
||||
expires = messages["fwarn_list_expired"].format(warn["expires"])
|
||||
else:
|
||||
expires = messages["fwarn_view_expires"].format(warn["expires"])
|
||||
else:
|
||||
expires = messages["fwarn_never_expires"]
|
||||
if warn["expired"]:
|
||||
start = "\u000314"
|
||||
end = " [\u00037{0}\u000314]\u0003".format(messages["fwarn_expired"])
|
||||
if not warn["ack"]:
|
||||
ack = "\u0002!\u0002 "
|
||||
cli.notice(nick, messages["warn_list"].format(
|
||||
start, ack, warn["id"], warn["issued"], warn["reason"], warn["amount"], expires, end))
|
||||
if i == 0:
|
||||
cli.notice(nick, messages["fwarn_list_empty"])
|
||||
return
|
||||
|
||||
if command == "view":
|
||||
try:
|
||||
warn_id = int(params.pop(0))
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["warn_view_syntax"])
|
||||
return
|
||||
|
||||
acc, hm = parse_warning_target(nick)
|
||||
warning = db.get_warning(warn_id, acc, hm)
|
||||
if warning is None:
|
||||
reply(cli, nick, chan, messages["fwarn_invalid_warning"])
|
||||
return
|
||||
|
||||
if warning["expired"]:
|
||||
expires = messages["fwarn_view_expired"].format(warning["expires"])
|
||||
elif warning["expires"] is None:
|
||||
expires = messages["fwarn_view_active"].format(messages["fwarn_never_expires"])
|
||||
else:
|
||||
expires = messages["fwarn_view_active"].format(messages["fwarn_view_expires"].format(warning["expires"]))
|
||||
|
||||
cli.notice(nick, messages["warn_view_header"].format(
|
||||
warning["id"], warning["issued"], warning["amount"], expires))
|
||||
cli.notice(nick, warning["reason"])
|
||||
|
||||
sanctions = []
|
||||
if not warning["ack"]:
|
||||
sanctions.append(messages["warn_view_ack"].format(warning["id"]))
|
||||
if warning["sanctions"]:
|
||||
sanctions.append(messages["fwarn_view_sanctions"])
|
||||
if "stasis" in warning["sanctions"]:
|
||||
if warning["sanctions"]["stasis"] != 1:
|
||||
sanctions.append(messages["fwarn_view_stasis_plural"].format(warning["sanctions"]["stasis"]))
|
||||
else:
|
||||
sanctions.append(messages["fwarn_view_stasis_sing"])
|
||||
if "deny" in warning["sanctions"]:
|
||||
sanctions.append(messages["fwarn_view_deny"].format(", ".join(warning["sanctions"]["deny"])))
|
||||
if sanctions:
|
||||
cli.notice(nick, " ".join(sanctions))
|
||||
return
|
||||
|
||||
if command == "ack":
|
||||
try:
|
||||
warn_id = int(params.pop(0))
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["warn_ack_syntax"])
|
||||
return
|
||||
|
||||
acc, hm = parse_warning_target(nick)
|
||||
warning = db.get_warning(warn_id, acc, hm)
|
||||
if warning is None:
|
||||
reply(cli, nick, chan, messages["fwarn_invalid_warning"])
|
||||
return
|
||||
|
||||
db.acknowledge_warning(warn_id)
|
||||
reply(cli, nick, chan, messages["fwarn_done"])
|
||||
return
|
||||
|
||||
@cmd("fwarn", flag="A", pm=True)
|
||||
def fwarn(cli, nick, chan, rest):
|
||||
@ -8039,7 +8270,7 @@ def fwarn(cli, nick, chan, rest):
|
||||
target = None
|
||||
points = None
|
||||
need_ack = False
|
||||
expiry = None
|
||||
expires = None
|
||||
sanctions = {}
|
||||
reason = None
|
||||
notes = None
|
||||
@ -8050,6 +8281,10 @@ def fwarn(cli, nick, chan, rest):
|
||||
reply(cli, nick, chan, messages["fwarn_usage"])
|
||||
return
|
||||
|
||||
if command not in ("list", "view", "add", "del", "set", "help"):
|
||||
reply(cli, nick, chan, messages["fwarn_usage"])
|
||||
return
|
||||
|
||||
if command == "help":
|
||||
try:
|
||||
subcommand = params.pop(0)
|
||||
@ -8069,18 +8304,30 @@ def fwarn(cli, nick, chan, rest):
|
||||
list_all = params.pop(0)
|
||||
target = params.pop(0)
|
||||
page = int(params.pop(0))
|
||||
if list_all and list_all != "-all":
|
||||
if target is not None:
|
||||
page = int(target)
|
||||
target = list_all
|
||||
list_all = False
|
||||
elif show_all == "-all":
|
||||
list_all = True
|
||||
except IndexError:
|
||||
pass
|
||||
except ValueError:
|
||||
reply(cli, nick, chan, messages["fwarn_page_invalid"])
|
||||
return
|
||||
|
||||
try:
|
||||
if list_all and list_all != "-all":
|
||||
if target is not None:
|
||||
page = int(target)
|
||||
target = list_all
|
||||
list_all = False
|
||||
elif list_all == "-all":
|
||||
list_all = True
|
||||
except ValueError:
|
||||
reply(cli, nick, chan, messages["fwarn_page_invalid"])
|
||||
return
|
||||
|
||||
try:
|
||||
page = int(target)
|
||||
target = None
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
if target is not None:
|
||||
acc, hm = parse_warning_target(target)
|
||||
if acc is None and hm is None:
|
||||
@ -8088,7 +8335,7 @@ def fwarn(cli, nick, chan, rest):
|
||||
return
|
||||
warnings = db.list_warnings(acc, hm, list_all=list_all, skip=(page-1)*10, show=11)
|
||||
points = db.get_warning_points(acc, hm)
|
||||
reply(cli, nick, chan, messages["fwarn_list_header"].format(target, points))
|
||||
cli.notice(nick, messages["fwarn_list_header"].format(target, points))
|
||||
else:
|
||||
warnings = db.list_all_warnings(list_all=list_all, skip=(page-1)*10, show=11)
|
||||
|
||||
@ -8102,12 +8349,18 @@ def fwarn(cli, nick, chan, rest):
|
||||
if target is not None:
|
||||
parts.append(target)
|
||||
parts.append(str(page + 1))
|
||||
reply(cli, nick, chan, messages["fwarn_list_footer"].format(" ".join(parts)))
|
||||
cli.notice(nick, messages["fwarn_list_footer"].format(" ".join(parts)))
|
||||
break
|
||||
start = ""
|
||||
end = ""
|
||||
ack = ""
|
||||
expires = warn["expires"] if warn["expires"] is not None else messages["fwarn_never_expires"]
|
||||
if warn["expires"] is not None:
|
||||
if warn["expired"]:
|
||||
expires = messages["fwarn_list_expired"].format(warn["expires"])
|
||||
else:
|
||||
expires = messages["fwarn_view_expires"].format(warn["expires"])
|
||||
else:
|
||||
expires = messages["fwarn_never_expires"]
|
||||
if warn["deleted"]:
|
||||
start = "\u000314"
|
||||
end = " [\u00034{0}\u000314]\u0003".format(messages["fwarn_deleted"])
|
||||
@ -8116,11 +8369,11 @@ def fwarn(cli, nick, chan, rest):
|
||||
end = " [\u00037{0}\u000314]\u0003".format(messages["fwarn_expired"])
|
||||
if not warn["ack"]:
|
||||
ack = "\u0002!\u0002 "
|
||||
reply(cli, nick, chan, messages["fwarn_list"].format(
|
||||
cli.notice(nick, messages["fwarn_list"].format(
|
||||
start, ack, warn["id"], warn["issued"], warn["target"],
|
||||
warn["sender"], warn["reason"], warn["amount"], expires, end))
|
||||
if i == 0:
|
||||
reply(cli, nick, chan, messages["fwarn_list_empty"])
|
||||
cli.notice(nick, messages["fwarn_list_empty"])
|
||||
return
|
||||
|
||||
if command == "view":
|
||||
@ -8144,14 +8397,14 @@ def fwarn(cli, nick, chan, rest):
|
||||
else:
|
||||
expires = messages["fwarn_view_active"].format(messages["fwarn_view_expires"].format(warning["expires"]))
|
||||
|
||||
reply(cli, nick, chan, messages["fwarn_view_header"].format(
|
||||
cli.notice(nick, messages["fwarn_view_header"].format(
|
||||
warning["id"], warning["target"], warning["issued"], warning["sender"],
|
||||
warning["amount"], expires))
|
||||
|
||||
reason = [warning["reason"]]
|
||||
if warning["notes"] is not None:
|
||||
reason.append(warning["notes"])
|
||||
reply(cli, nick, chan, " | ".join(reason))
|
||||
cli.notice(nick, " | ".join(reason))
|
||||
|
||||
sanctions = []
|
||||
if not warning["ack"]:
|
||||
@ -8159,11 +8412,14 @@ def fwarn(cli, nick, chan, rest):
|
||||
if warning["sanctions"]:
|
||||
sanctions.append(messages["fwarn_view_sanctions"])
|
||||
if "stasis" in warning["sanctions"]:
|
||||
sanctions.append(messages["fwarn_view_stasis"].format(warning["sanctions"]["stasis"]))
|
||||
if warning["sanctions"]["stasis"] != 1:
|
||||
sanctions.append(messages["fwarn_view_stasis_plural"].format(warning["sanctions"]["stasis"]))
|
||||
else:
|
||||
sanctions.append(messages["fwarn_view_stasis_sing"])
|
||||
if "deny" in warning["sanctions"]:
|
||||
sanctions.append(messages["fwarn_view_deny"].format(", ".join(warning["sanctions"]["deny"])))
|
||||
if sanctions:
|
||||
reply(cli, nick, chan, " ".join(sanctions))
|
||||
cli.notice(nick, " ".join(sanctions))
|
||||
return
|
||||
|
||||
if command == "del":
|
||||
@ -8217,10 +8473,6 @@ def fwarn(cli, nick, chan, rest):
|
||||
reply(cli, nick, chan, messages["fwarn_done"])
|
||||
return
|
||||
|
||||
if command != "add":
|
||||
reply(cli, nick, chan, messages["fwarn_usage"])
|
||||
return
|
||||
|
||||
# command == "add"
|
||||
while params:
|
||||
p = params.pop(0)
|
||||
@ -8256,7 +8508,7 @@ def fwarn(cli, nick, chan, rest):
|
||||
if p == "~":
|
||||
reply(cli, nick, chan, messages["fwarn_syntax"])
|
||||
return
|
||||
expiry = p[1:]
|
||||
expires = p[1:]
|
||||
else:
|
||||
# sanctions are the only thing left here
|
||||
sanc = p.split("=", 1)
|
||||
@ -8303,12 +8555,22 @@ def fwarn(cli, nick, chan, rest):
|
||||
# convert expires into a proper datetime
|
||||
if expires is not None:
|
||||
suffix = expires[-1]
|
||||
try:
|
||||
amount = int(expires[:-1])
|
||||
except ValueError:
|
||||
reply(cli, nick, chan, messages["fwarn_expiry_invalid"])
|
||||
return
|
||||
|
||||
if amount <= 0:
|
||||
reply(cli, nick, chan, messages["fwarn_expiry_invalid"])
|
||||
return
|
||||
|
||||
if suffix == "d":
|
||||
expires = datetime.now() + timedelta(days=expires[:-1])
|
||||
expires = datetime.now() + timedelta(days=amount)
|
||||
elif suffix == "h":
|
||||
expires = datetime.now() + timedelta(hours=expires[:-1])
|
||||
expires = datetime.now() + timedelta(hours=amount)
|
||||
elif suffix == "m":
|
||||
expires = datetime.now() + timedelta(minutes=expires[:-1])
|
||||
expires = datetime.now() + timedelta(minutes=amount)
|
||||
else:
|
||||
reply(cli, nick, chan, messages["fwarn_expiry_invalid_suffix"])
|
||||
return
|
||||
|
Loading…
x
Reference in New Issue
Block a user