Add tempban sanction (#238)
Can be either time-based or points-based. Also, made fwarn a bit smarter at guessing what the user actually wanted to do based on the parameters given to it. Warnings now always require acknowledgement, because that paves the way for a future commit only beginning stasis once a warning is acknowledged. Warnings also split off into their own file to declutter wolfgame.py a bit (now only 9k lines, wooooo! >_>) Does not play nice with eir, that functionality isn't going to be in the bot itself but rather some custom code in lykos (hooking into privmsg).
This commit is contained in:
parent
c3698539c1
commit
cd3f9fc345
@ -790,17 +790,18 @@
|
||||
"warn_view_syntax": "Usage: warn view <id>",
|
||||
"warn_ack_syntax": "Usage: warn ack <id>",
|
||||
"warn_help_syntax": "Usage: warn help <subcommand>",
|
||||
"fwarn_add_syntax": "Usage: fwarn add <nick[!user@host]|=account> [@]<points> [~expiry] [sanctions] <:reason> [| notes]",
|
||||
"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, must be a number above 0 followed by either d, h, or m, or 'never' for a warning that never expires.",
|
||||
"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}.",
|
||||
"fwarn_done": "Done.",
|
||||
"fwarn_sanction_invalid": "Invalid sanction, can be either deny or stasis.",
|
||||
"fwarn_sanction_invalid": "Invalid sanction, can be either deny, stasis, or tempban.",
|
||||
"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_tempban_invalid": "Invalid tempban amount, specify sanction as \"tempban=number\" or \"tempban=expiration\" (followed by d, h, or m).",
|
||||
"fwarn_list_header": "{0} has {1} active warning point{2}. Warnings prefixed with \u0002!\u0002 are unacknowledged.",
|
||||
"warn_list_header": "You have {0} active warning point{1}. 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} point{8}, {9}){10}",
|
||||
@ -825,6 +826,7 @@
|
||||
"fwarn_view_stasis_sing": "1 game of stasis.",
|
||||
"fwarn_view_stasis_plural": "{0} games of stasis.",
|
||||
"fwarn_view_deny": "denied {0}.",
|
||||
"fwarn_view_tempban": "banned until {0} (if a number, indicates warning point threshold).",
|
||||
"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.",
|
||||
@ -846,6 +848,7 @@
|
||||
"part_warning": "Parting during game. You only have a short time to re-join the channel after parting to stay alive.",
|
||||
"quit_warning": "Quitting IRC during game. You only have a short time to re-join the channel after quitting to stay alive.",
|
||||
"acc_warning": "Changing accounts during game. Please do not change accounts while playing.",
|
||||
"tempban_kick": "{nick}",
|
||||
|
||||
"_": " vim: set sw=4 expandtab:"
|
||||
}
|
||||
|
121
src/db.py
121
src/db.py
@ -6,6 +6,7 @@ import sys
|
||||
import time
|
||||
from collections import defaultdict
|
||||
import threading
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
@ -13,7 +14,7 @@ from src.utilities import irc_lower, break_long_message, role_order, singular
|
||||
|
||||
# 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 = 2
|
||||
SCHEMA_VERSION = 3
|
||||
|
||||
_ts = threading.local()
|
||||
|
||||
@ -135,19 +136,37 @@ def decrement_stasis(acc=None, hostmask=None):
|
||||
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
|
||||
def set_stasis(newamt, acc=None, hostmask=None, relative=False):
|
||||
peid, plid = _get_ids(acc, hostmask, add=True)
|
||||
_set_stasis(int(newamt), peid, relative)
|
||||
|
||||
def _set_stasis(newamt, peid, relative=False):
|
||||
conn = _conn()
|
||||
with conn:
|
||||
c = conn.cursor()
|
||||
c.execute("""UPDATE person
|
||||
SET stasis_amount = MIN(stasis_amount, ?)
|
||||
WHERE id = ?""", (newamt, peid))
|
||||
c.execute("SELECT stasis_amount, stasis_expires FROM person WHERE id = ?", (peid,))
|
||||
oldamt, expiry = c.fetchone()
|
||||
if relative:
|
||||
newamt = oldamt + newamt
|
||||
if newamt < 0:
|
||||
newamt = 0
|
||||
if newamt > oldamt:
|
||||
delta = newamt - oldamt
|
||||
# increasing stasis, so need to update expiry
|
||||
c.execute("""UPDATE person
|
||||
SET
|
||||
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(delta)), (newamt, peid))
|
||||
else:
|
||||
# decreasing stasis, don't touch expiry
|
||||
c.execute("""UPDATE person
|
||||
SET stasis_amount = ?,
|
||||
WHERE id = ?""", (newamt, peid))
|
||||
|
||||
def expire_stasis():
|
||||
conn = _conn()
|
||||
@ -617,10 +636,9 @@ def get_warning_sanctions(warn_id):
|
||||
|
||||
return sanctions
|
||||
|
||||
def add_warning(tacc, thm, sacc, shm, amount, reason, notes, expires, need_ack):
|
||||
def add_warning(tacc, thm, sacc, shm, amount, reason, notes, expires):
|
||||
teid, tlid = _get_ids(tacc, thm, add=True)
|
||||
seid, slid = _get_ids(sacc, shm)
|
||||
ack = 0 if need_ack else 1
|
||||
conn = _conn()
|
||||
with conn:
|
||||
c = conn.cursor()
|
||||
@ -636,8 +654,8 @@ def add_warning(tacc, thm, sacc, shm, amount, reason, notes, expires, need_ack):
|
||||
?, ?, ?,
|
||||
datetime('now'), ?,
|
||||
?, ?,
|
||||
?
|
||||
)""", (teid, seid, amount, expires, reason, notes, ack))
|
||||
0
|
||||
)""", (teid, seid, amount, expires, reason, notes))
|
||||
return c.lastrowid
|
||||
|
||||
def add_warning_sanction(warning, sanction, data):
|
||||
@ -652,15 +670,28 @@ def add_warning_sanction(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))
|
||||
_set_stasis(int(data), peid, relative=True)
|
||||
elif sanction == "tempban":
|
||||
# we want to return a list of all banned accounts/hostmasks
|
||||
idlist = set()
|
||||
acclist = set()
|
||||
hmlist = set()
|
||||
c.execute("SELECT target FROM warning WHERE id = ?", (warning,))
|
||||
peid = c.fetchone()[0]
|
||||
c.execute("SELECT id, account, hostmask FROM player WHERE person = ? AND active = 1", (peid,))
|
||||
if isinstance(data, datetime):
|
||||
sql = "INSERT INTO bantrack (player, expires) values (?, ?)"
|
||||
else:
|
||||
sql = "INSERT INTO bantrack (player, warning_amount) values (?, ?)"
|
||||
for row in c:
|
||||
idlist.add(row[0])
|
||||
if row[1] is None:
|
||||
hmlist.add(row[2])
|
||||
else:
|
||||
acclist.add(row[1])
|
||||
for plid in idlist:
|
||||
c.execute(sql, (plid, data))
|
||||
return (acclist, hmlist)
|
||||
|
||||
def del_warning(warning, acc, hm):
|
||||
peid, plid = _get_ids(acc, hm)
|
||||
@ -691,6 +722,46 @@ def acknowledge_warning(warning):
|
||||
c = conn.cursor()
|
||||
c.execute("UPDATE warning SET acknowledged = 1 WHERE id = ?", (warning,))
|
||||
|
||||
def expire_tempbans():
|
||||
conn = _conn()
|
||||
with conn:
|
||||
idlist = set()
|
||||
acclist = set()
|
||||
hmlist = set()
|
||||
c = conn.cursor()
|
||||
c.execute("""SELECT
|
||||
bt.player,
|
||||
pl.account,
|
||||
pl.hostmask
|
||||
FROM bantrack bt
|
||||
JOIN player pl
|
||||
ON pl.id = bt.player
|
||||
WHERE
|
||||
(bt.expires IS NOT NULL AND bt.expires < datetime('now'))
|
||||
OR (
|
||||
warning_amount IS NOT NULL
|
||||
AND warning_amount <= (
|
||||
SELECT COALESCE(SUM(amount), 0)
|
||||
FROM warning
|
||||
WHERE
|
||||
target = pl.person
|
||||
AND deleted = 0
|
||||
AND (
|
||||
expires IS NULL
|
||||
OR expires > datetime('now')
|
||||
)
|
||||
)
|
||||
)""")
|
||||
for row in c:
|
||||
idlist.add(row[0])
|
||||
if row[1] is None:
|
||||
hmlist.add(row[2])
|
||||
else:
|
||||
acclist.add(row[1])
|
||||
for plid in idlist:
|
||||
c.execute("DELETE FROM bantrack WHERE player = ?", (plid,))
|
||||
return (acclist, hmlist)
|
||||
|
||||
def get_pre_restart_state():
|
||||
conn = _conn()
|
||||
with conn:
|
||||
@ -741,6 +812,10 @@ def _upgrade(oldversion):
|
||||
# player id as a string). When nocasing players, this may cause some records to be merged.
|
||||
with open(os.path.join(dn, "db", "upgrade2.sql"), "rt") as f:
|
||||
c.executescript(f.read())
|
||||
if oldversion < 3:
|
||||
print ("Upgrade from version 2 to 3...", file=sys.stderr)
|
||||
with open(os.path.join(dn, "db", "upgrade3.sql"), "rt") as f:
|
||||
c.executescript(f.read())
|
||||
|
||||
c.execute("PRAGMA user_version = " + str(SCHEMA_VERSION))
|
||||
print ("Upgrades complete!", file=sys.stderr)
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
-- Player tracking. This is just what the bot decides is a unique player, two entries
|
||||
-- here may end up corresponding to the same actual person (see below).
|
||||
CREATE TABLE IF NOT EXISTS player (
|
||||
CREATE TABLE player (
|
||||
id INTEGER PRIMARY KEY,
|
||||
-- What person this player record belongs to
|
||||
person INTEGER REFERENCES person(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
@ -17,13 +17,13 @@ CREATE TABLE IF NOT EXISTS player (
|
||||
active BOOLEAN NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS player_idx ON player (account, hostmask, active);
|
||||
CREATE INDEX IF NOT EXISTS person_idx ON player (person);
|
||||
CREATE INDEX player_idx ON player (account, hostmask, active);
|
||||
CREATE INDEX person_idx ON player (person);
|
||||
|
||||
-- Person tracking; a person can consist of multiple players (for example, someone may have
|
||||
-- an account player for when they are logged in and 3 hostmask players for when they are
|
||||
-- logged out depending on what connection they are using).
|
||||
CREATE TABLE IF NOT EXISTS person (
|
||||
CREATE TABLE person (
|
||||
id INTEGER PRIMARY KEY,
|
||||
-- Primary player for this person
|
||||
primary_player INTEGER NOT NULL UNIQUE REFERENCES player(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
@ -45,7 +45,7 @@ CREATE TABLE IF NOT EXISTS person (
|
||||
-- Sometimes people are bad, this keeps track of that for the purpose of automatically applying
|
||||
-- various sanctions and viewing the past history of someone. Outside of specifically-marked
|
||||
-- fields, records are never modified or deleted from this table once inserted.
|
||||
CREATE TABLE IF NOT EXISTS warning (
|
||||
CREATE TABLE warning (
|
||||
id INTEGER PRIMARY KEY,
|
||||
-- The target (recipient) of the warning
|
||||
target INTEGER NOT NULL REFERENCES person(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
@ -73,13 +73,13 @@ CREATE TABLE IF NOT EXISTS warning (
|
||||
deleted_on DATETIME
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS warning_idx ON warning (target, deleted, issued);
|
||||
CREATE INDEX IF NOT EXISTS warning_sender_idx ON warning (target, sender, deleted, issued);
|
||||
CREATE INDEX warning_idx ON warning (target, deleted, issued);
|
||||
CREATE INDEX warning_sender_idx ON warning (target, sender, deleted, issued);
|
||||
|
||||
-- In addition to giving warning points, a warning may have specific sanctions attached
|
||||
-- that apply until the warning expires; for example preventing a user from joining deadchat
|
||||
-- or denying them access to a particular command (such as !goat).
|
||||
CREATE TABLE IF NOT EXISTS warning_sanction (
|
||||
CREATE TABLE warning_sanction (
|
||||
-- The warning this sanction is attached to
|
||||
warning INTEGER NOT NULL REFERENCES warning(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
-- The type of sanction this is
|
||||
@ -92,7 +92,7 @@ CREATE TABLE IF NOT EXISTS warning_sanction (
|
||||
-- This shouldn't be too horribly slow, but if it is some strategies can be employed to speed it up:
|
||||
-- On startup, aggregate everything from this table and store in-memory, then increment those in-memory
|
||||
-- counts as games are played.
|
||||
CREATE TABLE IF NOT EXISTS game (
|
||||
CREATE TABLE game (
|
||||
id INTEGER PRIMARY KEY,
|
||||
-- The gamemode played
|
||||
gamemode TEXT NOT NULL COLLATE NOCASE,
|
||||
@ -110,10 +110,10 @@ CREATE TABLE IF NOT EXISTS game (
|
||||
winner TEXT COLLATE NOCASE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS game_idx ON game (gamemode, gamesize);
|
||||
CREATE INDEX game_idx ON game (gamemode, gamesize);
|
||||
|
||||
-- List of people who played in each game
|
||||
CREATE TABLE IF NOT EXISTS game_player (
|
||||
CREATE TABLE game_player (
|
||||
id INTEGER PRIMARY KEY,
|
||||
game INTEGER NOT NULL REFERENCES game(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
player INTEGER NOT NULL REFERENCES player(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
@ -125,11 +125,11 @@ CREATE TABLE IF NOT EXISTS game_player (
|
||||
dced BOOLEAN NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS game_player_game_idx ON game_player (game);
|
||||
CREATE INDEX IF NOT EXISTS game_player_player_idx ON game_player (player);
|
||||
CREATE INDEX game_player_game_idx ON game_player (game);
|
||||
CREATE INDEX game_player_player_idx ON game_player (player);
|
||||
|
||||
-- List of all roles and other special qualities (e.g. lover, entranced, etc.) the player had in game
|
||||
CREATE TABLE IF NOT EXISTS game_player_role (
|
||||
CREATE TABLE game_player_role (
|
||||
game_player INTEGER NOT NULL REFERENCES game_player(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
-- Name of the role or other quality recorded
|
||||
role TEXT NOT NULL COLLATE NOCASE,
|
||||
@ -137,11 +137,11 @@ CREATE TABLE IF NOT EXISTS game_player_role (
|
||||
special BOOLEAN NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS game_player_role_idx ON game_player_role (game_player);
|
||||
CREATE INDEX game_player_role_idx ON game_player_role (game_player);
|
||||
|
||||
-- Access templates; instead of manually specifying flags, a template can be used to add a group of
|
||||
-- flags simultaneously.
|
||||
CREATE TABLE IF NOT EXISTS access_template (
|
||||
CREATE TABLE access_template (
|
||||
id INTEGER PRIMARY KEY,
|
||||
-- Template name, for display purposes
|
||||
name TEXT NOT NULL,
|
||||
@ -150,7 +150,7 @@ CREATE TABLE IF NOT EXISTS access_template (
|
||||
);
|
||||
|
||||
-- Access control, owners still need to be specified in botconfig, but everyone else goes here
|
||||
CREATE TABLE IF NOT EXISTS access (
|
||||
CREATE TABLE access (
|
||||
person INTEGER NOT NULL PRIMARY KEY REFERENCES person(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
-- Template to base this person's access on, or NULL if it is not based on a template
|
||||
template INTEGER REFERENCES access_template(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
@ -159,8 +159,15 @@ CREATE TABLE IF NOT EXISTS access (
|
||||
flags TEXT
|
||||
);
|
||||
|
||||
-- Holds bans that the bot is tracking (due to sanctions)
|
||||
CREATE TABLE bantrack (
|
||||
player INTEGER NOT NULL PRIMARY KEY REFERENCES player(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
expires DATETIME,
|
||||
warning_amount INTEGER
|
||||
);
|
||||
|
||||
-- Used to hold state between restarts
|
||||
CREATE TABLE IF NOT EXISTS pre_restart_state (
|
||||
CREATE TABLE pre_restart_state (
|
||||
-- List of players to ping after the bot comes back online
|
||||
players TEXT
|
||||
);
|
||||
|
8
src/db/upgrade3.sql
Normal file
8
src/db/upgrade3.sql
Normal file
@ -0,0 +1,8 @@
|
||||
-- upgrade script to migrate from version 2 to version 3
|
||||
|
||||
CREATE TABLE bantrack (
|
||||
player INTEGER NOT NULL PRIMARY KEY REFERENCES player(id) DEFERRABLE INITIALLY DEFERRED,
|
||||
expires DATETIME,
|
||||
warning_amount INTEGER
|
||||
);
|
||||
|
@ -57,6 +57,7 @@ DEVOICE_DURING_NIGHT = False
|
||||
ALWAYS_PM_ROLE = False
|
||||
QUIET_MODE = "q" # "q" or "b"
|
||||
QUIET_PREFIX = "" # "" or "~q:"
|
||||
ACCOUNT_PREFIX = "$a:" # "$a:" or "~a:"
|
||||
# The bot will automatically toggle those modes of people joining
|
||||
AUTO_TOGGLE_MODES = ""
|
||||
|
||||
@ -70,6 +71,9 @@ PART_EXPIRY = "30d"
|
||||
ACC_PENALTY = 1
|
||||
ACC_EXPIRY = "30d"
|
||||
|
||||
# If True, disallows adding stasis via !fstasis (requires warnings instead)
|
||||
RESTRICT_FSTASIS = True
|
||||
|
||||
# The formatting of this sucks, sorry. This is used to automatically apply sanctions to warning levels
|
||||
# When a user crosses from below the min threshold to min or above points, the listed sanctions apply
|
||||
# Sanctions also apply while moving within the same threshold bracket (such as from min to max)
|
||||
@ -77,13 +81,10 @@ ACC_EXPIRY = "30d"
|
||||
# Scalestasis applies stasis equal to the formula ax^2 + bx + c, where x is the number of warning points
|
||||
# Tempban number can either be a duration (ending in d, h, or m) or a number meaning it expires when
|
||||
# warning points fall below that threshold.
|
||||
# Tempban is currently not implemented and does nothing right now.
|
||||
AUTO_SANCTION = (
|
||||
#min max sanctions
|
||||
(1, 4, {"ack": True}),
|
||||
(5, 9, {"stasis": 1}),
|
||||
(10, 10, {"ack": True, "stasis": 3}),
|
||||
(11, 14, {"stasis": 3}),
|
||||
(10, 14, {"stasis": 3}),
|
||||
(15, 24, {"scalestasis": (0, 1, -10)}),
|
||||
(25, 25, {"tempban": 15})
|
||||
)
|
||||
|
@ -34,7 +34,7 @@ def mass_mode(cli, md_param, md_plain):
|
||||
arg1 = "".join(md_plain) + "".join(z[0])
|
||||
arg2 = " ".join(z[1]) # + " " + " ".join([x+"!*@*" for x in z[1]])
|
||||
cli.mode(botconfig.CHANNEL, arg1, arg2)
|
||||
else:
|
||||
elif md_plain:
|
||||
cli.mode(botconfig.CHANNEL, "".join(md_plain))
|
||||
|
||||
def mass_privmsg(cli, targets, msg, notice=False, privmsg=False):
|
||||
|
863
src/warnings.py
Normal file
863
src/warnings.py
Normal file
@ -0,0 +1,863 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src import db
|
||||
from src.utilities import *
|
||||
from src.decorators import cmd
|
||||
from src.events import Event
|
||||
from src.messages import messages
|
||||
|
||||
__all__ = ["is_user_stasised", "decrement_stasis", "parse_warning_target", "add_warning", "expire_tempbans"]
|
||||
|
||||
def is_user_stasised(nick):
|
||||
"""Checks if a user is in stasis. Returns a number of games in stasis."""
|
||||
|
||||
if nick in var.USERS:
|
||||
ident = irc_lower(var.USERS[nick]["ident"])
|
||||
host = var.USERS[nick]["host"].lower()
|
||||
acc = irc_lower(var.USERS[nick]["account"])
|
||||
else:
|
||||
return -1
|
||||
amount = 0
|
||||
if not var.DISABLE_ACCOUNTS and acc and acc != "*":
|
||||
if acc in var.STASISED_ACCS:
|
||||
amount = var.STASISED_ACCS[acc]
|
||||
for hostmask in var.STASISED:
|
||||
if match_hostmask(hostmask, nick, ident, host):
|
||||
amount = max(amount, var.STASISED[hostmask])
|
||||
return amount
|
||||
|
||||
def decrement_stasis(nick=None):
|
||||
if nick and nick in var.USERS:
|
||||
ident = irc_lower(var.USERS[nick]["ident"])
|
||||
host = var.USERS[nick]["host"].lower()
|
||||
acc = irc_lower(var.USERS[nick]["account"])
|
||||
# decrement account stasis even if accounts are disabled
|
||||
if acc in var.STASISED_ACCS:
|
||||
db.decrement_stasis(acc=acc)
|
||||
for hostmask in var.STASISED:
|
||||
if match_hostmask(hostmask, nick, ident, host):
|
||||
db.decrement_stasis(hostmask=hostmask)
|
||||
else:
|
||||
db.decrement_stasis()
|
||||
# Also expire any expired stasis and tempbans and update our tracking vars
|
||||
db.expire_stasis()
|
||||
db.init_vars()
|
||||
|
||||
def expire_tempbans(cli):
|
||||
acclist, hmlist = db.expire_tempbans()
|
||||
cmodes = []
|
||||
for acc in acclist:
|
||||
cmodes.append(("-b", "{0}{1}".format(var.ACCOUNT_PREFIX, acc)))
|
||||
for hm in hmlist:
|
||||
cmodes.append(("-b", "*!*@{0}".format(hm)))
|
||||
mass_mode(cli, cmodes, [])
|
||||
|
||||
def parse_warning_target(target, lower=False):
|
||||
if target[0] == "=":
|
||||
if var.DISABLE_ACCOUNTS:
|
||||
return (None, None)
|
||||
tacc = target[1:]
|
||||
thm = None
|
||||
if lower:
|
||||
tacc = irc_lower(tacc)
|
||||
elif target in var.USERS:
|
||||
tacc = var.USERS[target]["account"]
|
||||
ident = var.USERS[target]["ident"]
|
||||
host = var.USERS[target]["host"]
|
||||
if lower:
|
||||
tacc = irc_lower(tacc)
|
||||
ident = irc_lower(ident)
|
||||
host = host.lower()
|
||||
thm = target + "!" + ident + "@" + host
|
||||
elif "@" in target:
|
||||
tacc = None
|
||||
thm = target
|
||||
if lower:
|
||||
hml, hmr = thm.split("@", 1)
|
||||
thm = irc_lower(hml) + "@" + hmr.lower()
|
||||
elif not var.DISABLE_ACCOUNTS:
|
||||
tacc = target
|
||||
thm = None
|
||||
if lower:
|
||||
tacc = irc_lower(tacc)
|
||||
else:
|
||||
return (None, None)
|
||||
return (tacc, thm)
|
||||
|
||||
def add_warning(cli, target, amount, actor, reason, notes=None, expires=None, sanctions=None):
|
||||
# make 0-point warnings no-op successfully, otherwise we add warnings when things like PART_PENALTY is 0
|
||||
if amount == 0:
|
||||
return False
|
||||
|
||||
tacc, thm = parse_warning_target(target)
|
||||
if tacc is None and thm is None:
|
||||
return False
|
||||
|
||||
if actor not in var.USERS and actor != botconfig.NICK:
|
||||
return False
|
||||
sacc = None
|
||||
shm = None
|
||||
if actor in var.USERS:
|
||||
sacc = var.USERS[actor]["account"]
|
||||
shm = actor + "!" + var.USERS[actor]["ident"] + "@" + var.USERS[actor]["host"]
|
||||
|
||||
# Turn expires into a datetime if we were passed a string; note that no error checking is performed here
|
||||
if isinstance(expires, str):
|
||||
exp_suffix = expires[-1]
|
||||
exp_amount = int(expires[:-1])
|
||||
|
||||
if exp_suffix == "d":
|
||||
expires = datetime.utcnow() + timedelta(days=exp_amount)
|
||||
elif exp_suffix == "h":
|
||||
expires = datetime.utcnow() + timedelta(hours=exp_amount)
|
||||
elif exp_suffix == "m":
|
||||
expires = datetime.utcnow() + timedelta(minutes=exp_amount)
|
||||
else:
|
||||
raise ValueError("Invalid expiration string")
|
||||
elif isinstance(expires, int):
|
||||
expires = datetime.utcnow() + timedelta(days=expires)
|
||||
|
||||
# Round expires to the nearest minute (30s rounds up)
|
||||
if isinstance(expires, datetime):
|
||||
round_add = 0
|
||||
if expires.second >= 30:
|
||||
round_add = 1
|
||||
expires -= timedelta(seconds=expires.second, microseconds=expires.microsecond)
|
||||
expires += timedelta(minutes=round_add)
|
||||
|
||||
# determine if we need to automatically add any sanctions
|
||||
if sanctions is None:
|
||||
sanctions = {}
|
||||
prev = db.get_warning_points(tacc, thm)
|
||||
cur = prev + amount
|
||||
for (mn, mx, sanc) in var.AUTO_SANCTION:
|
||||
if (prev < mn and cur >= mn) or (prev >= mn and prev <= mx and cur <= mx):
|
||||
if "stasis" in sanc:
|
||||
if "stasis" not in sanctions:
|
||||
sanctions["stasis"] = sanc["stasis"]
|
||||
else:
|
||||
sanctions["stasis"] = max(sanctions["stasis"], sanc["stasis"])
|
||||
if "scalestasis" in sanc:
|
||||
(a, b, c) = sanc["scalestasis"]
|
||||
amt = (a * cur * cur) + (b * cur) + c
|
||||
if "stasis" not in sanctions:
|
||||
sanctions["stasis"] = amt
|
||||
else:
|
||||
sanctions["stasis"] = max(sanctions["stasis"], amt)
|
||||
if "deny" in sanc:
|
||||
if "deny" not in sanctions:
|
||||
sanctions["deny"] = set(sanc["deny"])
|
||||
else:
|
||||
sanctions["deny"].update(sanc["deny"])
|
||||
if "tempban" in sanc:
|
||||
# tempban's param can either be a fixed expiry time or a number
|
||||
# which indicates the warning point threshold that the ban will be lifted at
|
||||
# if two are set at once, the threshold takes precedence over set times
|
||||
# within each category, a larger set time or a lower threshold takes precedence
|
||||
exp = None
|
||||
ths = None
|
||||
if sanc["tempban"][-1] in ("d", "h", "m"):
|
||||
amt = int(sanc["tempban"][:-1])
|
||||
dur = sanc["tempban"][-1]
|
||||
if dur == "d":
|
||||
exp = datetime.utcnow() + timedelta(days=amt)
|
||||
elif dur == "h":
|
||||
exp = datetime.utcnow() + timedelta(hours=amt)
|
||||
elif dur == "m":
|
||||
exp = datetime.utcnow() + timedelta(minutes=amt)
|
||||
else:
|
||||
ths = int(sanc["tempban"])
|
||||
|
||||
if "tempban" in sanctions:
|
||||
if isinstance(sanctions["tempban"], datetime):
|
||||
if ths is not None:
|
||||
sanctions["tempban"] = ths
|
||||
else:
|
||||
sanctions["tempban"] = max(sanctions["tempban"], exp)
|
||||
elif ths is not None:
|
||||
sanctions["tempban"] = min(sanctions["tempban"], ths)
|
||||
elif ths is not None:
|
||||
sanctions["tempban"] = ths
|
||||
else:
|
||||
sanctions["tempban"] = exp
|
||||
|
||||
sid = db.add_warning(tacc, thm, sacc, shm, amount, reason, notes, expires)
|
||||
if "stasis" in sanctions:
|
||||
db.add_warning_sanction(sid, "stasis", sanctions["stasis"])
|
||||
if "deny" in sanctions:
|
||||
for cmd in sanctions["deny"]:
|
||||
db.add_warning_sanction(sid, "deny command", cmd)
|
||||
if "tempban" in sanctions:
|
||||
# this inserts into the bantrack table too
|
||||
(acclist, hmlist) = db.add_warning_sanction(sid, "tempban", sanctions["tempban"])
|
||||
cmodes = []
|
||||
for acc in acclist:
|
||||
cmodes.append(("+b", "{0}{1}".format(var.ACCOUNT_PREFIX, acc)))
|
||||
for hm in hmlist:
|
||||
cmodes.append(("+b", "*!*@{0}".format(hm)))
|
||||
mass_mode(cli, cmodes, [])
|
||||
for (nick, user) in var.USERS.items():
|
||||
if user["account"] in acclist:
|
||||
cli.kick(botconfig.CHANNEL, nick, messages["tempban_kick"].format(nick=nick, botnick=botconfig.NICK, reason=reason))
|
||||
elif user["host"] in hmlist:
|
||||
cli.kick(botconfig.CHANNEL, nick, messages["tempban_kick"].format(nick=nick, botnick=botconfig.NICK, reason=reason))
|
||||
|
||||
# Update any tracking vars that may have changed due to this
|
||||
db.init_vars()
|
||||
|
||||
return sid
|
||||
|
||||
@cmd("stasis", chan=True, pm=True)
|
||||
def stasis(cli, nick, chan, rest):
|
||||
st = is_user_stasised(nick)
|
||||
if st:
|
||||
msg = messages["your_current_stasis"].format(st, "" if st == 1 else "s")
|
||||
else:
|
||||
msg = messages["you_not_in_stasis"]
|
||||
|
||||
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], lower=True)
|
||||
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 and var.RESTRICT_FSTASIS:
|
||||
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.set_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] [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.
|
||||
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)
|
||||
reply(cli, nick, chan, messages["warn_list_header"].format(points, "" if points == 1 else "s"), private=True)
|
||||
|
||||
i = 0
|
||||
for warn in warnings:
|
||||
i += 1
|
||||
if (i == 11):
|
||||
parts = []
|
||||
if list_all:
|
||||
parts.append("-all")
|
||||
parts.append(str(page + 1))
|
||||
reply(cli, nick, chan, messages["warn_list_footer"].format(" ".join(parts)), private=True)
|
||||
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 "
|
||||
reply(cli, nick, chan, messages["warn_list"].format(
|
||||
start, ack, warn["id"], warn["issued"], warn["reason"], warn["amount"],
|
||||
"" if warn["amount"] == 1 else "s", expires, end), private=True)
|
||||
if i == 0:
|
||||
reply(cli, nick, chan, messages["fwarn_list_empty"], private=True)
|
||||
return
|
||||
|
||||
if command == "view":
|
||||
try:
|
||||
warn_id = params.pop(0)
|
||||
if warn_id[0] == "#":
|
||||
warn_id = warn_id[1:]
|
||||
warn_id = int(warn_id)
|
||||
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"]))
|
||||
|
||||
reply(cli, nick, chan, messages["warn_view_header"].format(
|
||||
warning["id"], warning["issued"], warning["amount"],
|
||||
"" if warning["amount"] == 1 else "s", expires), private=True)
|
||||
reply(cli, nick, chan, warning["reason"], private=True)
|
||||
|
||||
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:
|
||||
reply(cli, nick, chan, " ".join(sanctions), private=True)
|
||||
return
|
||||
|
||||
if command == "ack":
|
||||
try:
|
||||
warn_id = params.pop(0)
|
||||
if warn_id[0] == "#":
|
||||
warn_id = warn_id[1:]
|
||||
warn_id = int(warn_id)
|
||||
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="F", pm=True)
|
||||
def fwarn(cli, nick, chan, rest):
|
||||
"""Issues a warning to someone or views warnings."""
|
||||
# !fwarn list [-all] [nick] [page]
|
||||
# -all => Shows all warnings, if omitted only shows active (non-expired and non-deleted) ones.
|
||||
# nick => nick to view warnings for. Can also be a hostmask in nick!user@host form. If nick
|
||||
# is not online, interpreted as an account name. To specify an account if nick is online,
|
||||
# use =account. If not specified, shows all warnings on the bot.
|
||||
# !fwarn view <id> - views details on warning id
|
||||
# !fwarn del <id> - deletes warning id
|
||||
# !fwarn set <id> [~expiry] [reason] [| notes]
|
||||
# !fwarn add <nick> <points> [~expiry] [sanctions] [:]<reason> [| notes]
|
||||
# e.g. !fwarn add lykos 1 ~30d deny=goat,gstats stasis=5 Spamming | I secretly just hate him
|
||||
# nick => nick to warn. Can also be a hostmask in nick!user@host form. If nick is not online,
|
||||
# interpreted as an account name. To specify an account if nick is online, use =account.
|
||||
# points => Warning points, must be above 0
|
||||
# ~expiry => Expiration time, must be suffixed with d (days), h (hours), or m (minutes)
|
||||
# sanctions => list of sanctions. Valid sanctions are:
|
||||
# deny: denies access to the listed commands
|
||||
# stasis: gives the user stasis
|
||||
# reason => Reason, required. If the first word of the reason is also a sanction, prefix it with :
|
||||
# |notes => Secret notes, not shown to the user (only shown if viewing the warning in PM)
|
||||
# If specified, must be prefixed with |. This means | is not a valid character for use
|
||||
# in reasons (no escaping is performed).
|
||||
|
||||
params = re.split(" +", rest)
|
||||
target = None
|
||||
points = None
|
||||
expires = None
|
||||
sanctions = {}
|
||||
reason = None
|
||||
notes = None
|
||||
|
||||
try:
|
||||
command = params.pop(0)
|
||||
except IndexError:
|
||||
reply(cli, nick, chan, messages["fwarn_usage"])
|
||||
return
|
||||
|
||||
if command not in ("list", "view", "add", "del", "set", "help"):
|
||||
# if what follows is a number, assume we're viewing or setting a warning
|
||||
# (depending on number of params)
|
||||
# if it's another string, assume we're adding or listing, again depending
|
||||
# on number of params
|
||||
params.insert(0, command)
|
||||
try:
|
||||
num = int(command)
|
||||
if len(params) == 1:
|
||||
command = "view"
|
||||
else:
|
||||
command = "set"
|
||||
except ValueError:
|
||||
if len(params) < 3 or params[1] == "-all":
|
||||
command = "list"
|
||||
if len(params) > 1 and params[1] == "-all":
|
||||
# fwarn list expects these two params in a different order
|
||||
params.pop(1)
|
||||
params.insert(0, "-all")
|
||||
else:
|
||||
command = "add"
|
||||
|
||||
if command == "help":
|
||||
try:
|
||||
subcommand = params.pop(0)
|
||||
except IndexError:
|
||||
reply(cli, nick, chan, messages["fwarn_help_syntax"])
|
||||
return
|
||||
if subcommand not in ("list", "view", "add", "del", "set", "help"):
|
||||
reply(cli, nick, chan, messages["fwarn_usage"])
|
||||
return
|
||||
reply(cli, nick, chan, messages["fwarn_{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":
|
||||
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:
|
||||
reply(cli, nick, chan, messages["fwarn_nick_invalid"])
|
||||
return
|
||||
warnings = db.list_warnings(acc, hm, expired=list_all, deleted=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, "" if points == 1 else "s"), private=True)
|
||||
else:
|
||||
warnings = db.list_all_warnings(list_all=list_all, skip=(page-1)*10, show=11)
|
||||
|
||||
i = 0
|
||||
for warn in warnings:
|
||||
i += 1
|
||||
if (i == 11):
|
||||
parts = []
|
||||
if list_all:
|
||||
parts.append("-all")
|
||||
if target is not None:
|
||||
parts.append(target)
|
||||
parts.append(str(page + 1))
|
||||
reply(cli, nick, chan, messages["fwarn_list_footer"].format(" ".join(parts)), private=True)
|
||||
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["deleted"]:
|
||||
start = "\u000314"
|
||||
end = " [\u00034{0}\u000314]\u0003".format(messages["fwarn_deleted"])
|
||||
elif warn["expired"]:
|
||||
start = "\u000314"
|
||||
end = " [\u00037{0}\u000314]\u0003".format(messages["fwarn_expired"])
|
||||
if not warn["ack"]:
|
||||
ack = "\u0002!\u0002 "
|
||||
reply(cli, nick, chan, messages["fwarn_list"].format(
|
||||
start, ack, warn["id"], warn["issued"], warn["target"],
|
||||
warn["sender"], warn["reason"], warn["amount"],
|
||||
"" if warn["amount"] == 1 else "s", expires, end), private=True)
|
||||
if i == 0:
|
||||
reply(cli, nick, chan, messages["fwarn_list_empty"], private=True)
|
||||
return
|
||||
|
||||
if command == "view":
|
||||
try:
|
||||
warn_id = params.pop(0)
|
||||
if warn_id[0] == "#":
|
||||
warn_id = warn_id[1:]
|
||||
warn_id = int(warn_id)
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["fwarn_view_syntax"])
|
||||
return
|
||||
|
||||
warning = db.get_warning(warn_id)
|
||||
if warning is None:
|
||||
reply(cli, nick, chan, messages["fwarn_invalid_warning"])
|
||||
return
|
||||
|
||||
if warning["deleted"]:
|
||||
expires = messages["fwarn_view_deleted"].format(warning["deleted_on"], warning["deleted_by"])
|
||||
elif 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"]))
|
||||
|
||||
reply(cli, nick, chan, messages["fwarn_view_header"].format(
|
||||
warning["id"], warning["target"], warning["issued"], warning["sender"],
|
||||
warning["amount"], "" if warning["amount"] == 1 else "s", expires), private=True)
|
||||
|
||||
reason = [warning["reason"]]
|
||||
if warning["notes"] is not None:
|
||||
reason.append(warning["notes"])
|
||||
reply(cli, nick, chan, " | ".join(reason), private=True)
|
||||
|
||||
sanctions = []
|
||||
if not warning["ack"]:
|
||||
sanctions.append(messages["fwarn_view_ack"])
|
||||
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 "tempban" in warning["sanctions"]:
|
||||
sanctions.append(messages["fwarn_view_tempban"].format(warning["sanctions"]["tempban"]))
|
||||
if sanctions:
|
||||
reply(cli, nick, chan, " ".join(sanctions), private=True)
|
||||
return
|
||||
|
||||
if command == "del":
|
||||
try:
|
||||
warn_id = params.pop(0)
|
||||
if warn_id[0] == "#":
|
||||
warn_id = warn_id[1:]
|
||||
warn_id = int(warn_id)
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["fwarn_del_syntax"])
|
||||
return
|
||||
|
||||
warning = db.get_warning(warn_id)
|
||||
if warning is None:
|
||||
reply(cli, nick, chan, messages["fwarn_invalid_warning"])
|
||||
return
|
||||
|
||||
acc, hm = parse_warning_target(nick)
|
||||
db.del_warning(warn_id, acc, hm)
|
||||
reply(cli, nick, chan, messages["fwarn_done"])
|
||||
return
|
||||
|
||||
if command == "set":
|
||||
try:
|
||||
warn_id = params.pop(0)
|
||||
if warn_id[0] == "#":
|
||||
warn_id = warn_id[1:]
|
||||
warn_id = int(warn_id)
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["fwarn_set_syntax"])
|
||||
return
|
||||
|
||||
warning = db.get_warning(warn_id)
|
||||
if warning is None:
|
||||
reply(cli, nick, chan, messages["fwarn_invalid_warning"])
|
||||
return
|
||||
|
||||
rsp = " ".join(params).split("|", 1)
|
||||
if len(rsp) == 1:
|
||||
rsp.append(None)
|
||||
reason, notes = rsp
|
||||
reason = reason.strip()
|
||||
|
||||
# check for modified expiry
|
||||
expires = warning["expires"]
|
||||
rsp = reason.split(" ", 1)
|
||||
if rsp[0] and rsp[0][0] == "~":
|
||||
if len(rsp) == 1:
|
||||
rsp.append("")
|
||||
expires, reason = rsp
|
||||
expires = expires[1:]
|
||||
reason = reason.strip()
|
||||
|
||||
if expires in messages["never_aliases"]:
|
||||
expires = None
|
||||
else:
|
||||
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
|
||||
|
||||
issued = datetime.strptime(warning["issued"], "%Y-%m-%d %H:%M:%S")
|
||||
if suffix == "d":
|
||||
expires = issued + timedelta(days=amount)
|
||||
elif suffix == "h":
|
||||
expires = issued + timedelta(hours=amount)
|
||||
elif suffix == "m":
|
||||
expires = issued + timedelta(minutes=amount)
|
||||
else:
|
||||
reply(cli, nick, chan, messages["fwarn_expiry_invalid"])
|
||||
return
|
||||
|
||||
round_add = 0
|
||||
if expires.second >= 30:
|
||||
round_add = 1
|
||||
expires -= timedelta(seconds=expires.second, microseconds=expires.microsecond)
|
||||
expires += timedelta(minutes=round_add)
|
||||
|
||||
# maintain existing reason if none was specified
|
||||
if not reason:
|
||||
reason = warning["reason"]
|
||||
|
||||
# maintain existing notes if none were specified
|
||||
if notes is not None:
|
||||
notes = notes.strip()
|
||||
if not notes:
|
||||
notes = None
|
||||
else:
|
||||
notes = warning["notes"]
|
||||
|
||||
db.set_warning(warn_id, expires, reason, notes)
|
||||
reply(cli, nick, chan, messages["fwarn_done"])
|
||||
return
|
||||
|
||||
# command == "add"
|
||||
while params:
|
||||
p = params.pop(0)
|
||||
if target is None:
|
||||
# figuring out what target actually is is handled in add_warning
|
||||
target = p
|
||||
elif points is None:
|
||||
try:
|
||||
points = int(p)
|
||||
except ValueError:
|
||||
reply(cli, nick, chan, messages["fwarn_points_invalid"])
|
||||
return
|
||||
if points < 1:
|
||||
reply(cli, nick, chan, messages["fwarn_points_invalid"])
|
||||
return
|
||||
elif notes is not None:
|
||||
notes += " " + p
|
||||
elif reason is not None:
|
||||
rsp = p.split("|", 1)
|
||||
if len(rsp) > 1:
|
||||
notes = rsp[1]
|
||||
reason += " " + rsp[0]
|
||||
elif p[0] == ":":
|
||||
if p == ":":
|
||||
reason = ""
|
||||
else:
|
||||
reason = p[1:]
|
||||
elif p[0] == "~":
|
||||
if p == "~":
|
||||
reply(cli, nick, chan, messages["fwarn_syntax"])
|
||||
return
|
||||
expires = p[1:]
|
||||
else:
|
||||
# sanctions are the only thing left here
|
||||
sanc = p.split("=", 1)
|
||||
if sanc[0] == "deny":
|
||||
try:
|
||||
cmds = sanc[1].split(",")
|
||||
normalized_cmds = set()
|
||||
for cmd in cmds:
|
||||
normalized = None
|
||||
for obj in COMMANDS[cmd]:
|
||||
# do not allow denying in-game commands (vote, see, etc.)
|
||||
# this technically traps goat too, so special case that, as we want
|
||||
# goat to be deny-able. Furthermore, the warn command cannot be denied.
|
||||
if (not obj.playing and not obj.roles) or obj.name == "goat":
|
||||
normalized = obj.name
|
||||
if normalized == "warn":
|
||||
normalized = None
|
||||
if normalized is None:
|
||||
reply(cli, nick, chan, messages["fwarn_deny_invalid_command"].format(cmd))
|
||||
return
|
||||
normalized_cmds.add(normalized)
|
||||
sanctions["deny"] = normalized_cmds
|
||||
except IndexError:
|
||||
reply(cli, nick, chan, messages["fwarn_deny_invalid"])
|
||||
return
|
||||
elif sanc[0] == "stasis":
|
||||
try:
|
||||
sanctions["stasis"] = int(sanc[1])
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["fwarn_stasis_invalid"])
|
||||
return
|
||||
elif sanc[0] == "tempban":
|
||||
try:
|
||||
banamt = sanc[1]
|
||||
suffix = banamt[-1]
|
||||
if suffix not in ("d", "h", "m"):
|
||||
sanctions["tempban"] = int(banamt)
|
||||
else:
|
||||
banamt = int(banamt[:-1])
|
||||
if suffix == "d":
|
||||
sanctions["tempban"] = datetime.utcnow() + timedelta(days=banamt)
|
||||
elif suffix == "h":
|
||||
sanctions["tempban"] = datetime.utcnow() + timedelta(hours=banamt)
|
||||
elif suffix == "m":
|
||||
sanctions["tempban"] = datetime.utcnow() + timedelta(minutes=banamt)
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["fwarn_tempban_invalid"])
|
||||
return
|
||||
else:
|
||||
# not a valid sanction, assume this is the start of the reason
|
||||
reason = p
|
||||
|
||||
if target is None or points is None or reason is None:
|
||||
reply(cli, nick, chan, messages["fwarn_add_syntax"])
|
||||
return
|
||||
|
||||
reason = reason.strip()
|
||||
if notes is not None:
|
||||
notes = notes.strip()
|
||||
|
||||
# convert expires into a proper datetime
|
||||
if expires is None:
|
||||
expires = var.DEFAULT_EXPIRY
|
||||
|
||||
if expires.lower() in messages["never_aliases"]:
|
||||
expires = None
|
||||
|
||||
try:
|
||||
warn_id = add_warning(cli, target, points, nick, reason, notes, expires, sanctions)
|
||||
except ValueError:
|
||||
reply(cli, nick, chan, messages["fwarn_expiry_invalid"])
|
||||
|
||||
if warn_id is False:
|
||||
reply(cli, nick, chan, messages["fwarn_cannot_add"])
|
||||
else:
|
||||
reply(cli, nick, chan, messages["fwarn_added"].format(warn_id))
|
||||
|
||||
|
||||
# vim: set sw=4 expandtab:
|
789
src/wolfgame.py
789
src/wolfgame.py
@ -29,7 +29,6 @@ import random
|
||||
import re
|
||||
import signal
|
||||
import socket
|
||||
import sqlite3
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
@ -47,6 +46,7 @@ import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import db, decorators, events, logger, proxy, debuglog, errlog, plog
|
||||
from src.messages import messages
|
||||
from src.warnings import *
|
||||
|
||||
# done this way so that events is accessible in !eval (useful for debugging)
|
||||
Event = events.Event
|
||||
@ -215,6 +215,9 @@ def connect_callback(cli):
|
||||
for nick in to_be_devoiced:
|
||||
cmodes.append(("-v", nick))
|
||||
|
||||
# Expire tempbans
|
||||
expire_tempbans(cli)
|
||||
|
||||
# If the bot was restarted in the middle of the join phase, ping players that were joined.
|
||||
players = db.get_pre_restart_state()
|
||||
if players:
|
||||
@ -1268,6 +1271,7 @@ def kill_join(cli, chan):
|
||||
# use this opportunity to expire pending stasis
|
||||
db.expire_stasis()
|
||||
db.init_vars()
|
||||
expire_tempbans(cli)
|
||||
if var.AFTER_FLASTGAME is not None:
|
||||
var.AFTER_FLASTGAME()
|
||||
var.AFTER_FLASTGAME = None
|
||||
@ -1364,7 +1368,7 @@ def fleave(cli, nick, chan, rest):
|
||||
if a in rset:
|
||||
var.ORIGINAL_ROLES[r].remove(a)
|
||||
var.ORIGINAL_ROLES[r].add("(dced)"+a)
|
||||
add_warning(a, var.LEAVE_PENALTY, botconfig.NICK, messages["leave_warning"], expires=var.LEAVE_EXPIRY)
|
||||
add_warning(cli, a, var.LEAVE_PENALTY, botconfig.NICK, messages["leave_warning"], expires=var.LEAVE_EXPIRY)
|
||||
if a in var.PLAYERS:
|
||||
var.DCED_PLAYERS[a] = var.PLAYERS.pop(a)
|
||||
|
||||
@ -2681,8 +2685,8 @@ def stop_game(cli, winner="", abort=False, additional_winners=None, log=True):
|
||||
mass_privmsg(cli, var.DEADCHAT_PLAYERS, messages["endgame_deadchat"].format(chan))
|
||||
|
||||
reset_modes_timers(cli)
|
||||
|
||||
reset()
|
||||
expire_tempbans(cli)
|
||||
|
||||
# This must be after reset()
|
||||
if var.AFTER_FLASTGAME is not None:
|
||||
@ -3366,7 +3370,7 @@ def reaper(cli, gameid):
|
||||
if nck in rlist:
|
||||
var.ORIGINAL_ROLES[r].remove(nck)
|
||||
var.ORIGINAL_ROLES[r].add("(dced)"+nck)
|
||||
add_warning(nck, var.IDLE_PENALTY, botconfig.NICK, messages["idle_warning"], expires=var.IDLE_EXPIRY)
|
||||
add_warning(cli, nck, var.IDLE_PENALTY, botconfig.NICK, messages["idle_warning"], expires=var.IDLE_EXPIRY)
|
||||
del_player(cli, nck, end_game = False, death_triggers = False)
|
||||
chk_win(cli)
|
||||
pl = list_players()
|
||||
@ -3383,7 +3387,7 @@ def reaper(cli, gameid):
|
||||
else:
|
||||
cli.msg(chan, messages["quit_death_no_reveal"].format(dcedplayer))
|
||||
if var.PHASE != "join":
|
||||
add_warning(dcedplayer, var.PART_PENALTY, botconfig.NICK, messages["quit_warning"], expires=var.PART_EXPIRY)
|
||||
add_warning(cli, dcedplayer, var.PART_PENALTY, botconfig.NICK, messages["quit_warning"], expires=var.PART_EXPIRY)
|
||||
if not del_player(cli, dcedplayer, devoice = False, death_triggers = False):
|
||||
return
|
||||
elif what == "part" and (datetime.now() - timeofdc) > timedelta(seconds=var.PART_GRACE_TIME):
|
||||
@ -3392,7 +3396,7 @@ def reaper(cli, gameid):
|
||||
else:
|
||||
cli.msg(chan, messages["part_death_no_reveal"].format(dcedplayer))
|
||||
if var.PHASE != "join":
|
||||
add_warning(dcedplayer, var.PART_PENALTY, botconfig.NICK, messages["part_warning"], expires=var.PART_EXPIRY)
|
||||
add_warning(cli, dcedplayer, var.PART_PENALTY, botconfig.NICK, messages["part_warning"], expires=var.PART_EXPIRY)
|
||||
if not del_player(cli, dcedplayer, devoice = False, death_triggers = False):
|
||||
return
|
||||
elif what == "account" and (datetime.now() - timeofdc) > timedelta(seconds=var.ACC_GRACE_TIME):
|
||||
@ -3401,7 +3405,7 @@ def reaper(cli, gameid):
|
||||
else:
|
||||
cli.msg(chan, messages["account_death_no_reveal"].format(dcedplayer))
|
||||
if var.PHASE != "join":
|
||||
add_warning(dcedplayer, var.ACC_PENALTY, botconfig.NICK, messages["acc_warning"], expires=var.ACC_EXPIRY)
|
||||
add_warning(cli, dcedplayer, var.ACC_PENALTY, botconfig.NICK, messages["acc_warning"], expires=var.ACC_EXPIRY)
|
||||
if not del_player(cli, dcedplayer, devoice = False, death_triggers = False):
|
||||
return
|
||||
time.sleep(10)
|
||||
@ -3884,7 +3888,7 @@ def leave_game(cli, nick, chan, rest):
|
||||
if nick in rset:
|
||||
var.ORIGINAL_ROLES[r].remove(nick)
|
||||
var.ORIGINAL_ROLES[r].add("(dced)"+nick)
|
||||
add_warning(nick, var.LEAVE_PENALTY, botconfig.NICK, messages["leave_warning"], expires=var.LEAVE_EXPIRY)
|
||||
add_warning(cli, nick, var.LEAVE_PENALTY, botconfig.NICK, messages["leave_warning"], expires=var.LEAVE_EXPIRY)
|
||||
if nick in var.PLAYERS:
|
||||
var.DCED_PLAYERS[nick] = var.PLAYERS.pop(nick)
|
||||
|
||||
@ -7779,775 +7783,6 @@ def on_error(cli, pfx, msg):
|
||||
elif msg.startswith("Closing Link:"):
|
||||
raise SystemExit
|
||||
|
||||
def is_user_stasised(nick):
|
||||
"""Checks if a user is in stasis. Returns a number of games in stasis."""
|
||||
|
||||
if nick in var.USERS:
|
||||
ident = irc_lower(var.USERS[nick]["ident"])
|
||||
host = var.USERS[nick]["host"].lower()
|
||||
acc = irc_lower(var.USERS[nick]["account"])
|
||||
else:
|
||||
return -1
|
||||
amount = 0
|
||||
if not var.DISABLE_ACCOUNTS and acc and acc != "*":
|
||||
if acc in var.STASISED_ACCS:
|
||||
amount = var.STASISED_ACCS[acc]
|
||||
for hostmask in var.STASISED:
|
||||
if match_hostmask(hostmask, nick, ident, host):
|
||||
amount = max(amount, var.STASISED[hostmask])
|
||||
return amount
|
||||
|
||||
def decrement_stasis(nick=None):
|
||||
if nick and nick in var.USERS:
|
||||
ident = irc_lower(var.USERS[nick]["ident"])
|
||||
host = var.USERS[nick]["host"].lower()
|
||||
acc = irc_lower(var.USERS[nick]["account"])
|
||||
# decrement account stasis even if accounts are disabled
|
||||
if acc in var.STASISED_ACCS:
|
||||
db.decrement_stasis(acc=acc)
|
||||
for hostmask in var.STASISED:
|
||||
if match_hostmask(hostmask, nick, ident, host):
|
||||
db.decrement_stasis(hostmask=hostmask)
|
||||
else:
|
||||
db.decrement_stasis()
|
||||
# Also expire any expired stasis and update our tracking vars
|
||||
db.expire_stasis()
|
||||
db.init_vars()
|
||||
|
||||
def parse_warning_target(target, lower=False):
|
||||
if target[0] == "=":
|
||||
if var.DISABLE_ACCOUNTS:
|
||||
return (None, None)
|
||||
tacc = target[1:]
|
||||
thm = None
|
||||
if lower:
|
||||
tacc = irc_lower(tacc)
|
||||
elif target in var.USERS:
|
||||
tacc = var.USERS[target]["account"]
|
||||
ident = var.USERS[target]["ident"]
|
||||
host = var.USERS[target]["host"]
|
||||
if lower:
|
||||
tacc = irc_lower(tacc)
|
||||
ident = irc_lower(ident)
|
||||
host = host.lower()
|
||||
thm = target + "!" + ident + "@" + host
|
||||
elif "@" in target:
|
||||
tacc = None
|
||||
thm = target
|
||||
if lower:
|
||||
hml, hmr = thm.split("@", 1)
|
||||
thm = irc_lower(hml) + "@" + hmr.lower()
|
||||
elif not var.DISABLE_ACCOUNTS:
|
||||
tacc = target
|
||||
thm = None
|
||||
if lower:
|
||||
tacc = irc_lower(tacc)
|
||||
else:
|
||||
return (None, None)
|
||||
return (tacc, thm)
|
||||
|
||||
def add_warning(target, amount, actor, reason, notes=None, expires=None, need_ack=False, sanctions=None):
|
||||
# make 0-point warnings no-op successfully, otherwise we add warnings when things like PART_PENALTY is 0
|
||||
if amount == 0:
|
||||
return False
|
||||
|
||||
tacc, thm = parse_warning_target(target)
|
||||
if tacc is None and thm is None:
|
||||
return False
|
||||
|
||||
if actor not in var.USERS and actor != botconfig.NICK:
|
||||
return False
|
||||
sacc = None
|
||||
shm = None
|
||||
if actor in var.USERS:
|
||||
sacc = var.USERS[actor]["account"]
|
||||
shm = actor + "!" + var.USERS[actor]["ident"] + "@" + var.USERS[actor]["host"]
|
||||
|
||||
# Turn expires into a datetime if we were passed a string; note that no error checking is performed here
|
||||
if isinstance(expires, str):
|
||||
exp_suffix = expires[-1]
|
||||
exp_amount = int(expires[:-1])
|
||||
|
||||
if exp_suffix == "d":
|
||||
expires = datetime.utcnow() + timedelta(days=exp_amount)
|
||||
elif exp_suffix == "h":
|
||||
expires = datetime.utcnow() + timedelta(hours=exp_amount)
|
||||
elif exp_suffix == "m":
|
||||
expires = datetime.utcnow() + timedelta(minutes=exp_amount)
|
||||
else:
|
||||
raise ValueError("Invalid expiration string")
|
||||
elif isinstance(expires, int):
|
||||
expires = datetime.utcnow() + timedelta(days=expires)
|
||||
|
||||
# Round expires to the nearest minute (30s rounds up)
|
||||
if isinstance(expires, datetime):
|
||||
round_add = 0
|
||||
if expires.second >= 30:
|
||||
round_add = 1
|
||||
expires -= timedelta(seconds=expires.second, microseconds=expires.microsecond)
|
||||
expires += timedelta(minutes=round_add)
|
||||
|
||||
# determine if we need to automatically add any sanctions
|
||||
if sanctions is None:
|
||||
sanctions = {}
|
||||
prev = db.get_warning_points(tacc, thm)
|
||||
cur = prev + amount
|
||||
for (mn, mx, sanc) in var.AUTO_SANCTION:
|
||||
if (prev < mn and cur >= mn) or (prev >= mn and prev <= mx and cur <= mx):
|
||||
if "ack" in sanc:
|
||||
need_ack = True
|
||||
if "stasis" in sanc:
|
||||
if "stasis" not in sanctions:
|
||||
sanctions["stasis"] = sanc["stasis"]
|
||||
else:
|
||||
sanctions["stasis"] = max(sanctions["stasis"], sanc["stasis"])
|
||||
if "scalestasis" in sanc:
|
||||
(a, b, c) = sanc["scalestasis"]
|
||||
amt = (a * cur * cur) + (b * cur) + c
|
||||
if "stasis" not in sanctions:
|
||||
sanctions["stasis"] = amt
|
||||
else:
|
||||
sanctions["stasis"] = max(sanctions["stasis"], amt)
|
||||
if "deny" in sanc:
|
||||
if "deny" not in sanctions:
|
||||
sanctions["deny"] = set(sanc["deny"])
|
||||
else:
|
||||
sanctions["deny"].update(sanc["deny"])
|
||||
if "tempban" in sanc:
|
||||
# XXX: need to do this somehow, leaving here as a reminder for later
|
||||
pass
|
||||
|
||||
sid = db.add_warning(tacc, thm, sacc, shm, amount, reason, notes, expires, need_ack)
|
||||
if "stasis" in sanctions:
|
||||
db.add_warning_sanction(sid, "stasis", sanctions["stasis"])
|
||||
if "deny" in sanctions:
|
||||
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)
|
||||
def stasis(cli, nick, chan, rest):
|
||||
st = is_user_stasised(nick)
|
||||
if st:
|
||||
msg = messages["your_current_stasis"].format(st, "" if st == 1 else "s")
|
||||
else:
|
||||
msg = messages["you_not_in_stasis"]
|
||||
|
||||
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], lower=True)
|
||||
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] [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.
|
||||
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)
|
||||
reply(cli, nick, chan, messages["warn_list_header"].format(points, "" if points == 1 else "s"), private=True)
|
||||
|
||||
i = 0
|
||||
for warn in warnings:
|
||||
i += 1
|
||||
if (i == 11):
|
||||
parts = []
|
||||
if list_all:
|
||||
parts.append("-all")
|
||||
parts.append(str(page + 1))
|
||||
reply(cli, nick, chan, messages["warn_list_footer"].format(" ".join(parts)), private=True)
|
||||
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 "
|
||||
reply(cli, nick, chan, messages["warn_list"].format(
|
||||
start, ack, warn["id"], warn["issued"], warn["reason"], warn["amount"],
|
||||
"" if warn["amount"] == 1 else "s", expires, end), private=True)
|
||||
if i == 0:
|
||||
reply(cli, nick, chan, messages["fwarn_list_empty"], private=True)
|
||||
return
|
||||
|
||||
if command == "view":
|
||||
try:
|
||||
warn_id = params.pop(0)
|
||||
if warn_id[0] == "#":
|
||||
warn_id = warn_id[1:]
|
||||
warn_id = int(warn_id)
|
||||
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"]))
|
||||
|
||||
reply(cli, nick, chan, messages["warn_view_header"].format(
|
||||
warning["id"], warning["issued"], warning["amount"],
|
||||
"" if warning["amount"] == 1 else "s", expires), private=True)
|
||||
reply(cli, nick, chan, warning["reason"], private=True)
|
||||
|
||||
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:
|
||||
reply(cli, nick, chan, " ".join(sanctions), private=True)
|
||||
return
|
||||
|
||||
if command == "ack":
|
||||
try:
|
||||
warn_id = params.pop(0)
|
||||
if warn_id[0] == "#":
|
||||
warn_id = warn_id[1:]
|
||||
warn_id = int(warn_id)
|
||||
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="F", pm=True)
|
||||
def fwarn(cli, nick, chan, rest):
|
||||
"""Issues a warning to someone or views warnings."""
|
||||
# !fwarn list [-all] [nick] [page]
|
||||
# -all => Shows all warnings, if omitted only shows active (non-expired and non-deleted) ones.
|
||||
# nick => nick to view warnings for. Can also be a hostmask in nick!user@host form. If nick
|
||||
# is not online, interpreted as an account name. To specify an account if nick is online,
|
||||
# use =account. If not specified, shows all warnings on the bot.
|
||||
# !fwarn view <id> - views details on warning id
|
||||
# !fwarn del <id> - deletes warning id
|
||||
# !fwarn set <id> [~expiry] [reason] [| notes]
|
||||
# !fwarn add <nick> [@]<points> [~expiry] [sanctions] <:reason> [| notes]
|
||||
# e.g. !fwarn add lykos @1 ~30d deny=goat,gstats stasis=5 :Spamming | I secretly just hate him
|
||||
# nick => nick to warn. Can also be a hostmask in nick!user@host form. If nick is not online,
|
||||
# interpreted as an account name. To specify an account if nick is online, use =account.
|
||||
# @ => warning requires acknowledgement before user can !join again
|
||||
# points => Warning points, must be above 0
|
||||
# ~expiry => Expiration time, must be suffixed with d (days), h (hours), or m (minutes)
|
||||
# sanctions => list of sanctions. Valid sanctions are:
|
||||
# deny: denies access to the listed commands
|
||||
# stasis: gives the user stasis
|
||||
# :reason => Reason, required. Must be prefixed with :
|
||||
# |notes => Secret notes, not shown to the user (only shown if viewing the warning in PM)
|
||||
# If specified, must be prefixed with |. This means | is not a valid character for use
|
||||
# in reasons (no escaping is performed).
|
||||
|
||||
params = re.split(" +", rest)
|
||||
target = None
|
||||
points = None
|
||||
need_ack = False
|
||||
expires = None
|
||||
sanctions = {}
|
||||
reason = None
|
||||
notes = None
|
||||
|
||||
try:
|
||||
command = params.pop(0)
|
||||
except IndexError:
|
||||
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)
|
||||
except IndexError:
|
||||
reply(cli, nick, chan, messages["fwarn_help_syntax"])
|
||||
return
|
||||
if subcommand not in ("list", "view", "add", "del", "set", "help"):
|
||||
reply(cli, nick, chan, messages["fwarn_usage"])
|
||||
return
|
||||
reply(cli, nick, chan, messages["fwarn_{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":
|
||||
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:
|
||||
reply(cli, nick, chan, messages["fwarn_nick_invalid"])
|
||||
return
|
||||
warnings = db.list_warnings(acc, hm, expired=list_all, deleted=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, "" if points == 1 else "s"), private=True)
|
||||
else:
|
||||
warnings = db.list_all_warnings(list_all=list_all, skip=(page-1)*10, show=11)
|
||||
|
||||
i = 0
|
||||
for warn in warnings:
|
||||
i += 1
|
||||
if (i == 11):
|
||||
parts = []
|
||||
if list_all:
|
||||
parts.append("-all")
|
||||
if target is not None:
|
||||
parts.append(target)
|
||||
parts.append(str(page + 1))
|
||||
reply(cli, nick, chan, messages["fwarn_list_footer"].format(" ".join(parts)), private=True)
|
||||
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["deleted"]:
|
||||
start = "\u000314"
|
||||
end = " [\u00034{0}\u000314]\u0003".format(messages["fwarn_deleted"])
|
||||
elif warn["expired"]:
|
||||
start = "\u000314"
|
||||
end = " [\u00037{0}\u000314]\u0003".format(messages["fwarn_expired"])
|
||||
if not warn["ack"]:
|
||||
ack = "\u0002!\u0002 "
|
||||
reply(cli, nick, chan, messages["fwarn_list"].format(
|
||||
start, ack, warn["id"], warn["issued"], warn["target"],
|
||||
warn["sender"], warn["reason"], warn["amount"],
|
||||
"" if warn["amount"] == 1 else "s", expires, end), private=True)
|
||||
if i == 0:
|
||||
reply(cli, nick, chan, messages["fwarn_list_empty"], private=True)
|
||||
return
|
||||
|
||||
if command == "view":
|
||||
try:
|
||||
warn_id = params.pop(0)
|
||||
if warn_id[0] == "#":
|
||||
warn_id = warn_id[1:]
|
||||
warn_id = int(warn_id)
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["fwarn_view_syntax"])
|
||||
return
|
||||
|
||||
warning = db.get_warning(warn_id)
|
||||
if warning is None:
|
||||
reply(cli, nick, chan, messages["fwarn_invalid_warning"])
|
||||
return
|
||||
|
||||
if warning["deleted"]:
|
||||
expires = messages["fwarn_view_deleted"].format(warning["deleted_on"], warning["deleted_by"])
|
||||
elif 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"]))
|
||||
|
||||
reply(cli, nick, chan, messages["fwarn_view_header"].format(
|
||||
warning["id"], warning["target"], warning["issued"], warning["sender"],
|
||||
warning["amount"], "" if warning["amount"] == 1 else "s", expires), private=True)
|
||||
|
||||
reason = [warning["reason"]]
|
||||
if warning["notes"] is not None:
|
||||
reason.append(warning["notes"])
|
||||
reply(cli, nick, chan, " | ".join(reason), private=True)
|
||||
|
||||
sanctions = []
|
||||
if not warning["ack"]:
|
||||
sanctions.append(messages["fwarn_view_ack"])
|
||||
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:
|
||||
reply(cli, nick, chan, " ".join(sanctions), private=True)
|
||||
return
|
||||
|
||||
if command == "del":
|
||||
try:
|
||||
warn_id = params.pop(0)
|
||||
if warn_id[0] == "#":
|
||||
warn_id = warn_id[1:]
|
||||
warn_id = int(warn_id)
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["fwarn_del_syntax"])
|
||||
return
|
||||
|
||||
warning = db.get_warning(warn_id)
|
||||
if warning is None:
|
||||
reply(cli, nick, chan, messages["fwarn_invalid_warning"])
|
||||
return
|
||||
|
||||
acc, hm = parse_warning_target(nick)
|
||||
db.del_warning(warn_id, acc, hm)
|
||||
reply(cli, nick, chan, messages["fwarn_done"])
|
||||
return
|
||||
|
||||
if command == "set":
|
||||
try:
|
||||
warn_id = params.pop(0)
|
||||
if warn_id[0] == "#":
|
||||
warn_id = warn_id[1:]
|
||||
warn_id = int(warn_id)
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["fwarn_set_syntax"])
|
||||
return
|
||||
|
||||
warning = db.get_warning(warn_id)
|
||||
if warning is None:
|
||||
reply(cli, nick, chan, messages["fwarn_invalid_warning"])
|
||||
return
|
||||
|
||||
rsp = " ".join(params).split("|", 1)
|
||||
if len(rsp) == 1:
|
||||
rsp.append(None)
|
||||
reason, notes = rsp
|
||||
reason = reason.strip()
|
||||
|
||||
# check for modified expiry
|
||||
expires = warning["expires"]
|
||||
rsp = reason.split(" ", 1)
|
||||
if rsp[0] and rsp[0][0] == "~":
|
||||
if len(rsp) == 1:
|
||||
rsp.append("")
|
||||
expires, reason = rsp
|
||||
expires = expires[1:]
|
||||
reason = reason.strip()
|
||||
|
||||
if expires in messages["never_aliases"]:
|
||||
expires = None
|
||||
else:
|
||||
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
|
||||
|
||||
issued = datetime.strptime(warning["issued"], "%Y-%m-%d %H:%M:%S")
|
||||
if suffix == "d":
|
||||
expires = issued + timedelta(days=amount)
|
||||
elif suffix == "h":
|
||||
expires = issued + timedelta(hours=amount)
|
||||
elif suffix == "m":
|
||||
expires = issued + timedelta(minutes=amount)
|
||||
else:
|
||||
reply(cli, nick, chan, messages["fwarn_expiry_invalid"])
|
||||
return
|
||||
|
||||
round_add = 0
|
||||
if expires.second >= 30:
|
||||
round_add = 1
|
||||
expires -= timedelta(seconds=expires.second, microseconds=expires.microsecond)
|
||||
expires += timedelta(minutes=round_add)
|
||||
|
||||
# maintain existing reason if none was specified
|
||||
if not reason:
|
||||
reason = warning["reason"]
|
||||
|
||||
# maintain existing notes if none were specified
|
||||
if notes is not None:
|
||||
notes = notes.strip()
|
||||
if not notes:
|
||||
notes = None
|
||||
else:
|
||||
notes = warning["notes"]
|
||||
|
||||
db.set_warning(warn_id, expires, reason, notes)
|
||||
reply(cli, nick, chan, messages["fwarn_done"])
|
||||
return
|
||||
|
||||
# command == "add"
|
||||
while params:
|
||||
p = params.pop(0)
|
||||
if target is None:
|
||||
# figuring out what target actually is is handled in add_warning
|
||||
target = p
|
||||
elif points is None:
|
||||
points = p
|
||||
if points[0] == "@":
|
||||
points = points[1:]
|
||||
need_ack = True
|
||||
try:
|
||||
points = int(points)
|
||||
except ValueError:
|
||||
reply(cli, nick, chan, messages["fwarn_points_invalid"])
|
||||
return
|
||||
if points < 1:
|
||||
reply(cli, nick, chan, messages["fwarn_points_invalid"])
|
||||
return
|
||||
elif notes is not None:
|
||||
notes += " " + p
|
||||
elif reason is not None:
|
||||
rsp = p.split("|", 1)
|
||||
if len(rsp) > 1:
|
||||
notes = rsp[1]
|
||||
reason += " " + rsp[0]
|
||||
elif p[0] == ":":
|
||||
if p == ":":
|
||||
reason = ""
|
||||
else:
|
||||
reason = p[1:]
|
||||
elif p[0] == "~":
|
||||
if p == "~":
|
||||
reply(cli, nick, chan, messages["fwarn_syntax"])
|
||||
return
|
||||
expires = p[1:]
|
||||
else:
|
||||
# sanctions are the only thing left here
|
||||
sanc = p.split("=", 1)
|
||||
if sanc[0] == "deny":
|
||||
try:
|
||||
cmds = sanc[1].split(",")
|
||||
normalized_cmds = set()
|
||||
for cmd in cmds:
|
||||
normalized = None
|
||||
for obj in COMMANDS[cmd]:
|
||||
# do not allow denying in-game commands (vote, see, etc.)
|
||||
# this technically traps goat too, so special case that, as we want
|
||||
# goat to be deny-able. Furthermore, the warn command cannot be denied.
|
||||
if (not obj.playing and not obj.roles) or obj.name == "goat":
|
||||
normalized = obj.name
|
||||
if normalized == "warn":
|
||||
normalized = None
|
||||
if normalized is None:
|
||||
reply(cli, nick, chan, messages["fwarn_deny_invalid_command"].format(cmd))
|
||||
return
|
||||
normalized_cmds.add(normalized)
|
||||
sanctions["deny"] = normalized_cmds
|
||||
except IndexError:
|
||||
reply(cli, nick, chan, messages["fwarn_deny_invalid"])
|
||||
return
|
||||
elif sanc[0] == "stasis":
|
||||
try:
|
||||
sanctions["stasis"] = int(sanc[1])
|
||||
except (IndexError, ValueError):
|
||||
reply(cli, nick, chan, messages["fwarn_stasis_invalid"])
|
||||
return
|
||||
else:
|
||||
reply(cli, nick, chan, messages["fwarn_sanction_invalid"])
|
||||
return
|
||||
|
||||
if target is None or points is None or reason is None:
|
||||
reply(cli, nick, chan, messages["fwarn_add_syntax"])
|
||||
return
|
||||
|
||||
reason = reason.strip()
|
||||
if notes is not None:
|
||||
notes = notes.strip()
|
||||
|
||||
# convert expires into a proper datetime
|
||||
if expires is None:
|
||||
expires = var.DEFAULT_EXPIRY
|
||||
|
||||
if expires.lower() in messages["never_aliases"]:
|
||||
expires = None
|
||||
|
||||
try:
|
||||
warn_id = add_warning(target, points, nick, reason, notes, expires, need_ack, sanctions)
|
||||
except ValueError:
|
||||
reply(cli, nick, chan, messages["fwarn_expiry_invalid"])
|
||||
|
||||
if warn_id is False:
|
||||
reply(cli, nick, chan, messages["fwarn_cannot_add"])
|
||||
else:
|
||||
reply(cli, nick, chan, messages["fwarn_added"].format(warn_id))
|
||||
|
||||
@cmd("ftemplate", flag="F", pm=True)
|
||||
def ftemplate(cli, nick, chan, rest):
|
||||
params = re.split(" +", rest)
|
||||
|
Loading…
Reference in New Issue
Block a user