Make discrimination based on case ilegel
All hostmask and account comparisons are now case-insensitive (nicks still aren't, related to #217 -- changing nick sensitivity would break everything in numerous places). Also, refactor some things into other files where it makes sense to do so, because putting unrelated things into the same commit is fun.
This commit is contained in:
parent
1e21445e43
commit
1b7b2f6799
@ -4,34 +4,10 @@ import time
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src import logger
|
||||
from src.logger import stream, stream_handler, debuglog, errlog, plog
|
||||
from src import db
|
||||
|
||||
# Segue to logger, since src.gamemodes requires it
|
||||
# TODO: throw this into a logger.py perhaps so we aren't breaking up imports with non-import stuff
|
||||
def logger(file, write=True, display=True):
|
||||
if file is not None:
|
||||
open(file, "a").close() # create the file if it doesn't exist
|
||||
def log(*output, write=write, display=display):
|
||||
output = " ".join([str(x) for x in output]).replace("\u0002", "").replace("\\x02", "") # remove bold
|
||||
if botconfig.DEBUG_MODE:
|
||||
write = True
|
||||
if botconfig.DEBUG_MODE or botconfig.VERBOSE_MODE:
|
||||
display = True
|
||||
timestamp = get_timestamp()
|
||||
if display:
|
||||
print(timestamp + output, file=utf8stdout)
|
||||
if write and file is not None:
|
||||
with open(file, "a", errors="replace") as f:
|
||||
f.seek(0, 2)
|
||||
f.write(timestamp + output + "\n")
|
||||
|
||||
return log
|
||||
|
||||
stream_handler = logger(None)
|
||||
debuglog = logger("debug.log", write=False, display=False)
|
||||
errlog = logger("errors.log")
|
||||
plog = logger(None) # use this instead of print so that logs have timestamps
|
||||
|
||||
# Import the user-defined game modes
|
||||
# These are not required, so failing to import it doesn't matter
|
||||
# The file then imports our game modes
|
||||
@ -84,33 +60,4 @@ if args.normal: normal = True
|
||||
botconfig.DEBUG_MODE = debug_mode if not normal else False
|
||||
botconfig.VERBOSE_MODE = verbose if not normal else False
|
||||
|
||||
# Logger
|
||||
|
||||
# replace characters that can't be encoded with '?'
|
||||
# since windows likes to use weird encodings by default
|
||||
utf8stdout = open(1, 'w', errors="replace", closefd=False) # stdout
|
||||
|
||||
def get_timestamp(use_utc=None, ts_format=None):
|
||||
"""Return a timestamp with timezone + offset from UTC."""
|
||||
if use_utc is None:
|
||||
use_utc = botconfig.USE_UTC
|
||||
if ts_format is None:
|
||||
ts_format = botconfig.TIMESTAMP_FORMAT
|
||||
if use_utc:
|
||||
tmf = datetime.datetime.utcnow().strftime(ts_format)
|
||||
tz = "UTC"
|
||||
offset = "+0000"
|
||||
else:
|
||||
tmf = time.strftime(ts_format)
|
||||
tz = time.tzname[0]
|
||||
offset = "+"
|
||||
if datetime.datetime.utcnow().hour > datetime.datetime.now().hour:
|
||||
offset = "-"
|
||||
offset += str(time.timezone // 36).zfill(4)
|
||||
return tmf.format(tzname=tz, tzoffset=offset).strip().upper() + " "
|
||||
|
||||
def stream(output, level="normal"):
|
||||
if botconfig.VERBOSE_MODE or botconfig.DEBUG_MODE:
|
||||
stream_handler(output)
|
||||
elif level == "warning":
|
||||
stream_handler(output)
|
||||
# vim: set sw=4 expandtab:
|
||||
|
22
src/db.py
22
src/db.py
@ -1,5 +1,3 @@
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
import sqlite3
|
||||
import os
|
||||
import json
|
||||
@ -9,6 +7,10 @@ import time
|
||||
from collections import defaultdict
|
||||
import threading
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
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
|
||||
@ -57,6 +59,7 @@ def init_vars():
|
||||
|
||||
for acc, host, notice, simple, dc, pi, stasis, stasisexp, flags in c:
|
||||
if acc is not None:
|
||||
acc = irc_lower(acc)
|
||||
if simple == 1:
|
||||
var.SIMPLE_NOTIFY_ACCS.add(acc)
|
||||
if notice == 1:
|
||||
@ -71,6 +74,12 @@ def init_vars():
|
||||
if flags:
|
||||
var.FLAGS_ACCS[acc] = flags
|
||||
elif host is not None:
|
||||
# nick!ident lowercased per irc conventions, host uses normal casing
|
||||
try:
|
||||
hl, hr = host.split("@", 1)
|
||||
host = irc_lower(hl) + "@" + hr.lower()
|
||||
except ValueError:
|
||||
host = host.lower()
|
||||
if simple == 1:
|
||||
var.SIMPLE_NOTIFY.add(host)
|
||||
if notice == 1:
|
||||
@ -105,8 +114,10 @@ def init_vars():
|
||||
)""")
|
||||
for acc, host, command in c:
|
||||
if acc is not None:
|
||||
acc = irc_lower(acc)
|
||||
var.DENY_ACCS[acc].add(command)
|
||||
if host is not None:
|
||||
host = irc_lower(host)
|
||||
var.DENY[host].add(command)
|
||||
|
||||
def decrement_stasis(acc=None, hostmask=None):
|
||||
@ -324,13 +335,13 @@ def get_player_totals(acc, hostmask):
|
||||
totals = []
|
||||
for row in c:
|
||||
tmp[row[0]] = row[1]
|
||||
order = var.role_order()
|
||||
order = role_order()
|
||||
name = _get_display_name(peid)
|
||||
#ordered role stats
|
||||
totals = ["\u0002{0}\u0002: {1}".format(r, tmp[r]) for r in order if r in tmp]
|
||||
#lover or any other special stats
|
||||
totals += ["\u0002{0}\u0002: {1}".format(r, t) for r, t in tmp.items() if r not in order]
|
||||
return "\u0002{0}\u0002's totals | \u0002{1}\u0002 games | {2}".format(name, total_games, var.break_long_message(totals, ", "))
|
||||
return "\u0002{0}\u0002's totals | \u0002{1}\u0002 games | {2}".format(name, total_games, break_long_message(totals, ", "))
|
||||
|
||||
def get_game_stats(mode, size):
|
||||
conn = _conn()
|
||||
@ -356,7 +367,7 @@ def get_game_stats(mode, size):
|
||||
msg = "\u0002{0}\u0002 player games | {1}"
|
||||
bits = []
|
||||
for row in c:
|
||||
bits.append("%s wins: %d (%d%%)" % (var.singular(row[0]), row[1], round(row[1]/total_games * 100)))
|
||||
bits.append("%s wins: %d (%d%%)" % (singular(row[0]), row[1], round(row[1]/total_games * 100)))
|
||||
bits.append("total games: {0}".format(total_games))
|
||||
return msg.format(size, ", ".join(bits))
|
||||
|
||||
@ -872,6 +883,5 @@ elif ver < SCHEMA_VERSION:
|
||||
_upgrade(ver)
|
||||
|
||||
del need_install, conn, c, ver
|
||||
init_vars()
|
||||
|
||||
# vim: set expandtab:sw=4:ts=4:
|
||||
|
@ -10,11 +10,10 @@ from oyoyo.parse import parse_nick
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import logger, db
|
||||
from src import logger, errlog
|
||||
from src.messages import messages
|
||||
|
||||
adminlog = logger("audit.log")
|
||||
errlog = logger("errors.log")
|
||||
adminlog = logger.logger("audit.log")
|
||||
|
||||
COMMANDS = defaultdict(list)
|
||||
HOOKS = defaultdict(list)
|
||||
@ -135,9 +134,12 @@ class cmd:
|
||||
return
|
||||
|
||||
if nick in var.USERS and var.USERS[nick]["account"] != "*":
|
||||
acc = var.USERS[nick]["account"]
|
||||
acc = irc_lower(var.USERS[nick]["account"])
|
||||
else:
|
||||
acc = None
|
||||
nick = irc_lower(nick)
|
||||
ident = irc_lower(ident)
|
||||
host = host.lower()
|
||||
hostmask = nick + "!" + ident + "@" + host
|
||||
|
||||
if "" in self.cmds:
|
||||
@ -146,7 +148,7 @@ class cmd:
|
||||
if self.phases and var.PHASE not in self.phases:
|
||||
return
|
||||
|
||||
if self.playing and (nick not in var.list_players() or nick in var.DISCONNECTED):
|
||||
if self.playing and (nick not in list_players() or nick in var.DISCONNECTED):
|
||||
if chan == nick:
|
||||
pm(cli, nick, messages["player_not_playing"])
|
||||
else:
|
||||
@ -176,9 +178,9 @@ class cmd:
|
||||
forced_owner_only = True
|
||||
break
|
||||
|
||||
is_owner = var.is_owner(nick, ident, host)
|
||||
owner = is_owner(nick, ident, host)
|
||||
if self.owner_only or forced_owner_only:
|
||||
if is_owner:
|
||||
if owner:
|
||||
adminlog(chan, rawnick, self.name, rest)
|
||||
return self.func(*largs)
|
||||
|
||||
@ -189,8 +191,8 @@ class cmd:
|
||||
return
|
||||
|
||||
flags = var.FLAGS[hostmask] + var.FLAGS_ACCS[acc]
|
||||
is_full_admin = var.is_admin(nick, ident, host)
|
||||
if self.flag and (is_full_admin or is_owner):
|
||||
admin = is_admin(nick, ident, host)
|
||||
if self.flag and (admin or owner):
|
||||
adminlog(chan, rawnick, self.name, rest)
|
||||
return self.func(*largs)
|
||||
|
||||
|
@ -22,7 +22,7 @@ reset_roles = lambda i: OrderedDict([(role, (0,) * len(i)) for role in var.ROLE_
|
||||
|
||||
def get_lovers():
|
||||
lovers = []
|
||||
pl = var.list_players()
|
||||
pl = list_players()
|
||||
for lover in var.LOVERS:
|
||||
done = None
|
||||
for i, lset in enumerate(lovers):
|
||||
@ -65,12 +65,12 @@ class GameMode:
|
||||
pair, *pairs = pairs[0].split(",", 1)
|
||||
change = pair.lower().replace(":", " ").strip().rsplit(None, 1)
|
||||
if len(change) != 2:
|
||||
raise var.InvalidModeException(messages["invalid_mode_args"].format(arg))
|
||||
raise InvalidModeException(messages["invalid_mode_args"].format(arg))
|
||||
|
||||
key, val = change
|
||||
if key in ("role reveal", "reveal roles"):
|
||||
if val not in ("on", "off", "team"):
|
||||
raise var.InvalidModeException(messages["invalid_reveal"].format(val))
|
||||
raise InvalidModeException(messages["invalid_reveal"].format(val))
|
||||
self.ROLE_REVEAL = val
|
||||
if val == "off" and not hasattr(self, "STATS_TYPE"):
|
||||
self.STATS_TYPE = "disabled"
|
||||
@ -78,11 +78,11 @@ class GameMode:
|
||||
self.STATS_TYPE = "team"
|
||||
elif key in ("stats type", "stats"):
|
||||
if val not in ("default", "accurate", "team", "disabled"):
|
||||
raise var.InvalidModeException(messages["invalid_stats"].format(val))
|
||||
raise InvalidModeException(messages["invalid_stats"].format(val))
|
||||
self.STATS_TYPE = val
|
||||
elif key == "abstain":
|
||||
if val not in ("enabled", "restricted", "disabled"):
|
||||
raise var.InvalidModeException(messages["invalid_abstain"].format(val))
|
||||
raise InvalidModeException(messages["invalid_abstain"].format(val))
|
||||
if val == "enabled":
|
||||
self.ABSTAIN_ENABLED = True
|
||||
self.LIMIT_ABSTAIN = False
|
||||
@ -133,11 +133,11 @@ class ChangedRolesMode(GameMode):
|
||||
pair, *pairs = pairs[0].split(",", 1)
|
||||
change = pair.replace(":", " ").strip().rsplit(None, 1)
|
||||
if len(change) != 2:
|
||||
raise var.InvalidModeException(messages["invalid_mode_roles"].format(arg))
|
||||
raise InvalidModeException(messages["invalid_mode_roles"].format(arg))
|
||||
role, num = change
|
||||
try:
|
||||
if role.lower() in var.DISABLED_ROLES:
|
||||
raise var.InvalidModeException(messages["role_disabled"].format(role))
|
||||
raise InvalidModeException(messages["role_disabled"].format(role))
|
||||
elif role.lower() in self.ROLE_GUIDE:
|
||||
self.ROLE_GUIDE[role.lower()] = tuple([int(num)] * len(var.ROLE_INDEX))
|
||||
elif role.lower() == "default" and num.lower() in self.ROLE_GUIDE:
|
||||
@ -146,9 +146,9 @@ class ChangedRolesMode(GameMode):
|
||||
# handled in parent constructor
|
||||
pass
|
||||
else:
|
||||
raise var.InvalidModeException(messages["specific_invalid_role"].format(role))
|
||||
raise InvalidModeException(messages["specific_invalid_role"].format(role))
|
||||
except ValueError:
|
||||
raise var.InvalidModeException(messages["bad_role_value"])
|
||||
raise InvalidModeException(messages["bad_role_value"])
|
||||
|
||||
@game_mode("default", minp = 4, maxp = 24, likelihood = 20)
|
||||
class DefaultMode(GameMode):
|
||||
@ -198,7 +198,7 @@ class VillagergameMode(GameMode):
|
||||
def transition_day(self, evt, cli, var):
|
||||
# 30% chance we kill a safe, otherwise kill at random
|
||||
# when killing safes, go after seer, then harlot, then shaman
|
||||
pl = var.list_players()
|
||||
pl = list_players()
|
||||
tgt = None
|
||||
seer = None
|
||||
hlt = None
|
||||
@ -321,8 +321,8 @@ class EvilVillageMode(GameMode):
|
||||
events.remove_listener("chk_win", self.chk_win)
|
||||
|
||||
def chk_win(self, evt, var, lpl, lwolves, lrealwolves):
|
||||
lsafes = len(var.list_players(["oracle", "seer", "guardian angel", "shaman", "hunter", "villager"]))
|
||||
lcultists = len(var.list_players(["cultist"]))
|
||||
lsafes = len(list_players(["oracle", "seer", "guardian angel", "shaman", "hunter", "villager"]))
|
||||
lcultists = len(list_players(["cultist"]))
|
||||
evt.stop_processing = True
|
||||
|
||||
if lrealwolves == 0 and lsafes == 0:
|
||||
@ -728,7 +728,7 @@ class GuardianMode(GameMode):
|
||||
events.remove_listener("chk_win", self.chk_win)
|
||||
|
||||
def chk_win(self, evt, var, lpl, lwolves, lrealwolves):
|
||||
lguardians = len(var.list_players(["guardian angel", "bodyguard"]))
|
||||
lguardians = len(list_players(["guardian angel", "bodyguard"]))
|
||||
|
||||
if lpl < 1:
|
||||
# handled by default win cond checking
|
||||
@ -869,7 +869,7 @@ class SleepyMode(GameMode):
|
||||
self.do_nightmare = decorators.handle_error(self.do_nightmare)
|
||||
self.having_nightmare = True
|
||||
with var.WARNING_LOCK:
|
||||
t = threading.Timer(60, self.do_nightmare, (cli, var, random.choice(var.list_players()), var.NIGHT_COUNT))
|
||||
t = threading.Timer(60, self.do_nightmare, (cli, var, random.choice(list_players()), var.NIGHT_COUNT))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
else:
|
||||
@ -882,7 +882,7 @@ class SleepyMode(GameMode):
|
||||
def do_nightmare(self, cli, var, target, night):
|
||||
if var.PHASE != "night" or var.NIGHT_COUNT != night:
|
||||
return
|
||||
if target not in var.list_players():
|
||||
if target not in list_players():
|
||||
return
|
||||
self.having_nightmare = target
|
||||
pm(cli, self.having_nightmare, messages["sleepy_nightmare_begin"])
|
||||
@ -1082,7 +1082,7 @@ class SleepyMode(GameMode):
|
||||
|
||||
def nightmare_kill(self, evt, cli, var):
|
||||
# if True, it means night ended before 1 minute
|
||||
if self.having_nightmare is not None and self.having_nightmare is not True and self.having_nightmare in var.list_players():
|
||||
if self.having_nightmare is not None and self.having_nightmare is not True and self.having_nightmare in list_players():
|
||||
var.DYING.add(self.having_nightmare)
|
||||
pm(cli, self.having_nightmare, messages["sleepy_nightmare_death"])
|
||||
|
||||
@ -1158,10 +1158,10 @@ class MaelstromMode(GameMode):
|
||||
def _on_join(self, cli, var, nick, chan):
|
||||
role = random.choice(self.roles)
|
||||
|
||||
lpl = len(var.list_players()) + 1
|
||||
lwolves = len(var.list_players(var.WOLFCHAT_ROLES))
|
||||
lpl = len(list_players()) + 1
|
||||
lwolves = len(list_players(var.WOLFCHAT_ROLES))
|
||||
lcubs = len(var.ROLES["wolf cub"])
|
||||
lrealwolves = len(var.list_players(var.WOLF_ROLES)) - lcubs
|
||||
lrealwolves = len(list_players(var.WOLF_ROLES)) - lcubs
|
||||
lmonsters = len(var.ROLES["monster"])
|
||||
ldemoniacs = len(var.ROLES["demoniac"])
|
||||
ltraitors = len(var.ROLES["traitor"])
|
||||
@ -1196,7 +1196,7 @@ class MaelstromMode(GameMode):
|
||||
var.PLAYERS[nick] = var.USERS[nick]
|
||||
|
||||
if role == "doctor":
|
||||
lpl = len(var.list_players())
|
||||
lpl = len(list_players())
|
||||
var.DOCTORS[nick] = math.ceil(var.DOCTOR_IMMUNIZATION_MULTIPLIER * lpl)
|
||||
# let them know their role
|
||||
# FIXME: this is fugly
|
||||
@ -1206,12 +1206,12 @@ class MaelstromMode(GameMode):
|
||||
if role in var.WOLFCHAT_ROLES:
|
||||
relay_wolfchat_command(cli, nick, messages["wolfchat_new_member"].format(nick, role), var.WOLFCHAT_ROLES, is_wolf_command=True, is_kill_command=True)
|
||||
# TODO: make this part of !myrole instead, no reason we can't give out wofllist in that
|
||||
wolves = var.list_players(var.WOLFCHAT_ROLES)
|
||||
pl = var.list_players()
|
||||
wolves = list_players(var.WOLFCHAT_ROLES)
|
||||
pl = list_players()
|
||||
random.shuffle(pl)
|
||||
pl.remove(nick)
|
||||
for i, player in enumerate(pl):
|
||||
prole = var.get_role(player)
|
||||
prole = get_role(player)
|
||||
if prole in var.WOLFCHAT_ROLES:
|
||||
cursed = ""
|
||||
if player in var.ROLES["cursed villager"]:
|
||||
@ -1229,7 +1229,7 @@ class MaelstromMode(GameMode):
|
||||
# don't do this n1
|
||||
if var.FIRST_NIGHT:
|
||||
return
|
||||
villagers = var.list_players()
|
||||
villagers = list_players()
|
||||
lpl = len(villagers)
|
||||
addroles = self._role_attribution(cli, var, villagers, False)
|
||||
|
||||
|
@ -9,10 +9,8 @@ from oyoyo.parse import parse_nick
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src import decorators, logger, wolfgame
|
||||
|
||||
log = logger("errors.log")
|
||||
alog = logger(None)
|
||||
from src import decorators, wolfgame, errlog as log, stream_handler as alog
|
||||
from src.utilities import irc_equals
|
||||
|
||||
hook = decorators.hook
|
||||
|
||||
@ -38,11 +36,11 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
|
||||
log("Server did not send a case mapping; falling back to rfc1459.")
|
||||
var.CASEMAPPING = "rfc1459"
|
||||
|
||||
if (notice and ((not var.irc_equals(chan, botconfig.NICK) and not botconfig.ALLOW_NOTICE_COMMANDS) or
|
||||
(var.irc_equals(chan, botconfig.NICK) and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))):
|
||||
if (notice and ((not irc_equals(chan, botconfig.NICK) and not botconfig.ALLOW_NOTICE_COMMANDS) or
|
||||
(irc_equals(chan, botconfig.NICK) and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))):
|
||||
return # not allowed in settings
|
||||
|
||||
if var.irc_equals(chan, botconfig.NICK):
|
||||
if irc_equals(chan, botconfig.NICK):
|
||||
chan = parse_nick(rawnick)[0]
|
||||
|
||||
for fn in decorators.COMMANDS[""]:
|
||||
|
60
src/logger.py
Normal file
60
src/logger.py
Normal file
@ -0,0 +1,60 @@
|
||||
import datetime
|
||||
import time
|
||||
|
||||
import botconfig
|
||||
|
||||
def logger(file, write=True, display=True):
|
||||
if file is not None:
|
||||
open(file, "a").close() # create the file if it doesn't exist
|
||||
def log(*output, write=write, display=display):
|
||||
output = " ".join([str(x) for x in output]).replace("\u0002", "").replace("\\x02", "") # remove bold
|
||||
if botconfig.DEBUG_MODE:
|
||||
write = True
|
||||
if botconfig.DEBUG_MODE or botconfig.VERBOSE_MODE:
|
||||
display = True
|
||||
timestamp = get_timestamp()
|
||||
if display:
|
||||
print(timestamp + output, file=utf8stdout)
|
||||
if write and file is not None:
|
||||
with open(file, "a", errors="replace") as f:
|
||||
f.seek(0, 2)
|
||||
f.write(timestamp + output + "\n")
|
||||
|
||||
return log
|
||||
|
||||
stream_handler = logger(None)
|
||||
debuglog = logger("debug.log", write=False, display=False)
|
||||
errlog = logger("errors.log")
|
||||
plog = stream_handler # use this instead of print so that logs have timestamps
|
||||
|
||||
# replace characters that can't be encoded with '?'
|
||||
# since windows likes to use weird encodings by default
|
||||
utf8stdout = open(1, 'w', errors="replace", closefd=False) # stdout
|
||||
|
||||
def get_timestamp(use_utc=None, ts_format=None):
|
||||
"""Return a timestamp with timezone + offset from UTC."""
|
||||
if use_utc is None:
|
||||
use_utc = botconfig.USE_UTC
|
||||
if ts_format is None:
|
||||
ts_format = botconfig.TIMESTAMP_FORMAT
|
||||
if use_utc:
|
||||
tmf = datetime.datetime.utcnow().strftime(ts_format)
|
||||
tz = "UTC"
|
||||
offset = "+0000"
|
||||
else:
|
||||
tmf = time.strftime(ts_format)
|
||||
tz = time.tzname[0]
|
||||
offset = "+"
|
||||
if datetime.datetime.utcnow().hour > datetime.datetime.now().hour:
|
||||
offset = "-"
|
||||
offset += str(time.timezone // 36).zfill(4)
|
||||
return tmf.format(tzname=tz, tzoffset=offset).strip().upper() + " "
|
||||
|
||||
def stream(output, level="normal"):
|
||||
if botconfig.VERBOSE_MODE or botconfig.DEBUG_MODE:
|
||||
plog(output)
|
||||
elif level == "warning":
|
||||
plog(output)
|
||||
|
||||
|
||||
# vim: set sw=4 expandtab:
|
@ -1,7 +1,5 @@
|
||||
import inspect
|
||||
|
||||
from src.decorators import handle_error
|
||||
|
||||
""" This module introduces two decorators - @proxy.stub and @proxy.impl
|
||||
|
||||
@proxy.stub is used to decorate a stub method that should be filled in
|
||||
@ -61,6 +59,9 @@ def impl(f):
|
||||
_sigmatch(f)
|
||||
|
||||
# Always wrap proxy implementations in an error handler
|
||||
# proxy needs to be a top level (no dependencies) module, so can't import this
|
||||
# up top or else we get loops
|
||||
from src.decorators import handle_error
|
||||
IMPLS[f.__name__] = handle_error(f)
|
||||
# allows this method to be called directly in our module rather
|
||||
# than forcing use of the stub's module
|
||||
|
207
src/settings.py
207
src/settings.py
@ -320,211 +320,4 @@ 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!
|
||||
|
||||
is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol]
|
||||
|
||||
def match_hostmask(hostmask, nick, ident, host):
|
||||
# support n!u@h, u@h, or just h by itself
|
||||
matches = re.match('(?:(?:(.*?)!)?(.*?)@)?(.*)', hostmask.lower())
|
||||
|
||||
if ((not matches.group(1) or fnmatch.fnmatch(nick.lower(), matches.group(1))) and
|
||||
(not matches.group(2) or fnmatch.fnmatch(ident.lower(), matches.group(2))) and
|
||||
fnmatch.fnmatch(host.lower(), matches.group(3))):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_owner(nick, ident=None, host=None, acc=None):
|
||||
hosts = set(botconfig.OWNERS)
|
||||
accounts = set(botconfig.OWNERS_ACCOUNTS)
|
||||
if nick in USERS:
|
||||
if not ident:
|
||||
ident = USERS[nick]["ident"]
|
||||
if not host:
|
||||
host = USERS[nick]["host"]
|
||||
if not acc:
|
||||
acc = USERS[nick]["account"]
|
||||
|
||||
if not DISABLE_ACCOUNTS and acc and acc != "*":
|
||||
for pattern in accounts:
|
||||
if fnmatch.fnmatch(acc.lower(), pattern.lower()):
|
||||
return True
|
||||
|
||||
if host:
|
||||
for hostmask in hosts:
|
||||
if match_hostmask(hostmask, nick, ident, host):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_admin(nick, ident=None, host=None, acc=None):
|
||||
if nick in USERS:
|
||||
if not ident:
|
||||
ident = USERS[nick]["ident"]
|
||||
if not host:
|
||||
host = USERS[nick]["host"]
|
||||
if not acc:
|
||||
acc = USERS[nick]["account"]
|
||||
hostmask = nick + "!" + ident + "@" + host
|
||||
flags = FLAGS[hostmask] + FLAGS_ACCS[acc]
|
||||
|
||||
if not "F" in flags:
|
||||
try:
|
||||
hosts = set(botconfig.ADMINS)
|
||||
accounts = set(botconfig.ADMINS_ACCOUNTS)
|
||||
|
||||
if not DISABLE_ACCOUNTS and acc and acc != "*":
|
||||
for pattern in accounts:
|
||||
if fnmatch.fnmatch(acc.lower(), pattern.lower()):
|
||||
return True
|
||||
|
||||
if host:
|
||||
for hostmask in hosts:
|
||||
if match_hostmask(hostmask, nick, ident, host):
|
||||
return True
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return is_owner(nick, ident, host, acc)
|
||||
|
||||
return True
|
||||
|
||||
def irc_lower(nick):
|
||||
mapping = {
|
||||
"[": "{",
|
||||
"]": "}",
|
||||
"\\": "|",
|
||||
"^": "~",
|
||||
}
|
||||
|
||||
if CASEMAPPING == "strict-rfc1459":
|
||||
mapping.pop("^")
|
||||
elif CASEMAPPING == "ascii":
|
||||
mapping = {}
|
||||
|
||||
return nick.lower().translate(str.maketrans(mapping))
|
||||
|
||||
def irc_equals(nick1, nick2):
|
||||
return irc_lower(nick1) == irc_lower(nick2)
|
||||
|
||||
def plural(role, count=2):
|
||||
if count == 1:
|
||||
return role
|
||||
bits = role.split()
|
||||
if bits[-1][-2:] == "'s":
|
||||
bits[-1] = plural(bits[-1][:-2], count)
|
||||
bits[-1] += "'" if bits[-1][-1] == "s" else "'s"
|
||||
else:
|
||||
bits[-1] = {"person": "people",
|
||||
"wolf": "wolves",
|
||||
"has": "have",
|
||||
"succubus": "succubi",
|
||||
"child": "children"}.get(bits[-1], bits[-1] + "s")
|
||||
return " ".join(bits)
|
||||
|
||||
def singular(plural):
|
||||
# converse of plural above (kinda)
|
||||
# this is used to map plural team names back to singular,
|
||||
# so we don't need to worry about stuff like possessives
|
||||
# Note that this is currently only ever called on team names,
|
||||
# and will require adjustment if one wishes to use it on roles.
|
||||
conv = {"wolves": "wolf",
|
||||
"succubi": "succubus"}
|
||||
if plural in conv:
|
||||
return conv[plural]
|
||||
# otherwise we just added an s on the end
|
||||
return plural[:-1]
|
||||
|
||||
def list_players(roles = None):
|
||||
if roles is None:
|
||||
roles = ROLES.keys()
|
||||
pl = set()
|
||||
for x in roles:
|
||||
if x in TEMPLATE_RESTRICTIONS.keys():
|
||||
continue
|
||||
for p in ROLES.get(x, ()):
|
||||
pl.add(p)
|
||||
return [p for p in ALL_PLAYERS if p in pl]
|
||||
|
||||
def list_players_and_roles():
|
||||
plr = {}
|
||||
for x in ROLES.keys():
|
||||
if x in TEMPLATE_RESTRICTIONS.keys():
|
||||
continue # only get actual roles
|
||||
for p in ROLES[x]:
|
||||
plr[p] = x
|
||||
return plr
|
||||
|
||||
def get_role(p):
|
||||
for role, pl in ROLES.items():
|
||||
if role in TEMPLATE_RESTRICTIONS.keys():
|
||||
continue # only get actual roles
|
||||
if p in pl:
|
||||
return role
|
||||
|
||||
def get_reveal_role(nick):
|
||||
if HIDDEN_TRAITOR and get_role(nick) == "traitor":
|
||||
role = DEFAULT_ROLE
|
||||
elif HIDDEN_AMNESIAC and nick in ORIGINAL_ROLES["amnesiac"]:
|
||||
role = "amnesiac"
|
||||
elif HIDDEN_CLONE and nick in ORIGINAL_ROLES["clone"]:
|
||||
role = "clone"
|
||||
elif nick in WILD_CHILDREN:
|
||||
role = "wild child"
|
||||
else:
|
||||
role = get_role(nick)
|
||||
|
||||
if ROLE_REVEAL != "team":
|
||||
return role
|
||||
|
||||
if role in WOLFTEAM_ROLES:
|
||||
return "wolf"
|
||||
elif role in TRUE_NEUTRAL_ROLES:
|
||||
return "neutral player"
|
||||
else:
|
||||
return "villager"
|
||||
|
||||
def del_player(pname):
|
||||
prole = get_role(pname)
|
||||
ROLES[prole].remove(pname)
|
||||
tpls = get_templates(pname)
|
||||
for t in tpls:
|
||||
ROLES[t].remove(pname)
|
||||
if pname in BITTEN_ROLES:
|
||||
del BITTEN_ROLES[pname]
|
||||
if pname in CHARMED:
|
||||
CHARMED.remove(pname)
|
||||
|
||||
def get_templates(nick):
|
||||
tpl = []
|
||||
for x in TEMPLATE_RESTRICTIONS.keys():
|
||||
try:
|
||||
if nick in ROLES[x]:
|
||||
tpl.append(x)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return tpl
|
||||
|
||||
role_order = lambda: ROLE_GUIDE
|
||||
|
||||
def break_long_message(phrases, joinstr = " "):
|
||||
message = []
|
||||
count = 0
|
||||
for phrase in phrases:
|
||||
# IRC max is 512, but freenode splits around 380ish, make 300 to have plenty of wiggle room
|
||||
if count + len(joinstr) + len(phrase) > 300:
|
||||
message.append("\n" + phrase)
|
||||
count = len(phrase)
|
||||
else:
|
||||
if not message:
|
||||
count = len(phrase)
|
||||
else:
|
||||
count += len(joinstr) + len(phrase)
|
||||
message.append(phrase)
|
||||
return joinstr.join(message)
|
||||
|
||||
class InvalidModeException(Exception): pass
|
||||
|
||||
# vim: set sw=4 expandtab:
|
||||
|
228
src/utilities.py
228
src/utilities.py
@ -1,10 +1,11 @@
|
||||
import re
|
||||
import fnmatch
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src import proxy, debuglog
|
||||
|
||||
# message either privmsg or notice, depending on user settings
|
||||
# used in decorators (imported by proxy), so needs to go here
|
||||
def pm(cli, target, message):
|
||||
if is_fake_nick(target) and botconfig.DEBUG_MODE:
|
||||
debuglog("Would message fake nick {0}: {1!r}".format(target, message))
|
||||
@ -16,10 +17,6 @@ def pm(cli, target, message):
|
||||
|
||||
cli.msg(target, message)
|
||||
|
||||
from src import proxy, debuglog
|
||||
|
||||
# Some miscellaneous helper functions
|
||||
|
||||
is_fake_nick = re.compile(r"^[0-9]+$").search
|
||||
|
||||
def mass_mode(cli, md_param, md_plain):
|
||||
@ -86,7 +83,7 @@ def mass_privmsg(cli, targets, msg, notice=False, privmsg=False):
|
||||
def reply(cli, nick, chan, msg, private=False, prefix_nick=False):
|
||||
if chan == nick:
|
||||
pm(cli, nick, msg)
|
||||
elif private or (nick not in var.list_players() and var.PHASE in var.GAME_PHASES and chan == botconfig.CHANNEL):
|
||||
elif private or (nick not in list_players() and var.PHASE in var.GAME_PHASES and chan == botconfig.CHANNEL):
|
||||
cli.notice(nick, msg)
|
||||
else:
|
||||
if prefix_nick:
|
||||
@ -96,9 +93,9 @@ def reply(cli, nick, chan, msg, private=False, prefix_nick=False):
|
||||
|
||||
def is_user_simple(nick):
|
||||
if nick in var.USERS:
|
||||
ident = var.USERS[nick]["ident"]
|
||||
host = var.USERS[nick]["host"]
|
||||
acc = var.USERS[nick]["account"]
|
||||
ident = irc_lower(var.USERS[nick]["ident"])
|
||||
host = var.USERS[nick]["host"].lower()
|
||||
acc = irc_lower(var.USERS[nick]["account"])
|
||||
else:
|
||||
return False
|
||||
if acc and acc != "*" and not var.DISABLE_ACCOUNTS:
|
||||
@ -107,25 +104,25 @@ def is_user_simple(nick):
|
||||
return False
|
||||
elif not var.ACCOUNTS_ONLY:
|
||||
for hostmask in var.SIMPLE_NOTIFY:
|
||||
if var.match_hostmask(hostmask, nick, ident, host):
|
||||
if match_hostmask(hostmask, nick, ident, host):
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_user_notice(nick):
|
||||
if nick in var.USERS and var.USERS[nick]["account"] and var.USERS[nick]["account"] != "*" and not var.DISABLE_ACCOUNTS:
|
||||
if var.USERS[nick]["account"] in var.PREFER_NOTICE_ACCS:
|
||||
if irc_lower(var.USERS[nick]["account"]) in var.PREFER_NOTICE_ACCS:
|
||||
return True
|
||||
if nick in var.USERS and not var.ACCOUNTS_ONLY:
|
||||
ident = var.USERS[nick]["ident"]
|
||||
host = var.USERS[nick]["host"]
|
||||
ident = irc_lower(var.USERS[nick]["ident"])
|
||||
host = var.USERS[nick]["host"].lower()
|
||||
for hostmask in var.PREFER_NOTICE:
|
||||
if var.match_hostmask(hostmask, nick, ident, host):
|
||||
if match_hostmask(hostmask, nick, ident, host):
|
||||
return True
|
||||
return False
|
||||
|
||||
def in_wolflist(nick, who):
|
||||
myrole = var.get_role(nick)
|
||||
role = var.get_role(who)
|
||||
myrole = get_role(nick)
|
||||
role = get_role(who)
|
||||
wolves = var.WOLFCHAT_ROLES
|
||||
if var.RESTRICT_WOLFCHAT & var.RW_REM_NON_WOLVES:
|
||||
if var.RESTRICT_WOLFCHAT & var.RW_TRAITOR_NON_WOLF:
|
||||
@ -157,7 +154,7 @@ def relay_wolfchat_command(cli, nick, message, roles, is_wolf_command=False, is_
|
||||
else:
|
||||
wcroles = var.WOLF_ROLES | {"traitor"}
|
||||
|
||||
wcwolves = var.list_players(wcroles)
|
||||
wcwolves = list_players(wcroles)
|
||||
wcwolves.remove(nick)
|
||||
mass_privmsg(cli, wcwolves, message)
|
||||
mass_privmsg(cli, var.SPECTATING_WOLFCHAT, "[wolfchat] " + message)
|
||||
@ -174,4 +171,201 @@ def chk_decision(cli, force=""):
|
||||
def chk_win(cli, end_game=True, winner=None):
|
||||
pass
|
||||
|
||||
def irc_lower(nick):
|
||||
if nick is None:
|
||||
return None
|
||||
|
||||
mapping = {
|
||||
"[": "{",
|
||||
"]": "}",
|
||||
"\\": "|",
|
||||
"^": "~",
|
||||
}
|
||||
|
||||
if var.CASEMAPPING == "strict-rfc1459":
|
||||
mapping.pop("^")
|
||||
elif var.CASEMAPPING == "ascii":
|
||||
mapping = {}
|
||||
|
||||
return nick.lower().translate(str.maketrans(mapping))
|
||||
|
||||
def irc_equals(nick1, nick2):
|
||||
return irc_lower(nick1) == irc_lower(nick2)
|
||||
|
||||
is_role = lambda plyr, rol: rol in var.ROLES and plyr in var.ROLES[rol]
|
||||
|
||||
def match_hostmask(hostmask, nick, ident, host):
|
||||
# support n!u@h, u@h, or just h by itself
|
||||
matches = re.match('(?:(?:(.*?)!)?(.*?)@)?(.*)', hostmask)
|
||||
|
||||
if ((not matches.group(1) or fnmatch.fnmatch(irc_lower(nick), irc_lower(matches.group(1)))) and
|
||||
(not matches.group(2) or fnmatch.fnmatch(irc_lower(ident), irc_lower(matches.group(2)))) and
|
||||
fnmatch.fnmatch(host.lower(), matches.group(3).lower())):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_owner(nick, ident=None, host=None, acc=None):
|
||||
hosts = set(botconfig.OWNERS)
|
||||
accounts = set(botconfig.OWNERS_ACCOUNTS)
|
||||
if nick in var.USERS:
|
||||
if not ident:
|
||||
ident = var.USERS[nick]["ident"]
|
||||
if not host:
|
||||
host = var.USERS[nick]["host"]
|
||||
if not acc:
|
||||
acc = var.USERS[nick]["account"]
|
||||
|
||||
if not var.DISABLE_ACCOUNTS and acc and acc != "*":
|
||||
for pattern in accounts:
|
||||
if fnmatch.fnmatch(irc_lower(acc), irc_lower(pattern)):
|
||||
return True
|
||||
|
||||
if host:
|
||||
for hostmask in hosts:
|
||||
if match_hostmask(hostmask, nick, ident, host):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_admin(nick, ident=None, host=None, acc=None):
|
||||
if nick in var.USERS:
|
||||
if not ident:
|
||||
ident = var.USERS[nick]["ident"]
|
||||
if not host:
|
||||
host = var.USERS[nick]["host"]
|
||||
if not acc:
|
||||
acc = var.USERS[nick]["account"]
|
||||
acc = irc_lower(acc)
|
||||
hostmask = irc_lower(nick) + "!" + irc_lower(ident) + "@" + host.lower()
|
||||
flags = var.FLAGS[hostmask] + var.FLAGS_ACCS[acc]
|
||||
|
||||
if not "F" in flags:
|
||||
try:
|
||||
hosts = set(botconfig.ADMINS)
|
||||
accounts = set(botconfig.ADMINS_ACCOUNTS)
|
||||
|
||||
if not var.DISABLE_ACCOUNTS and acc and acc != "*":
|
||||
for pattern in accounts:
|
||||
if fnmatch.fnmatch(irc_lower(acc), irc_lower(pattern)):
|
||||
return True
|
||||
|
||||
if host:
|
||||
for hostmask in hosts:
|
||||
if match_hostmask(hostmask, nick, ident, host):
|
||||
return True
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return is_owner(nick, ident, host, acc)
|
||||
|
||||
return True
|
||||
|
||||
def plural(role, count=2):
|
||||
if count == 1:
|
||||
return role
|
||||
bits = role.split()
|
||||
if bits[-1][-2:] == "'s":
|
||||
bits[-1] = plural(bits[-1][:-2], count)
|
||||
bits[-1] += "'" if bits[-1][-1] == "s" else "'s"
|
||||
else:
|
||||
bits[-1] = {"person": "people",
|
||||
"wolf": "wolves",
|
||||
"has": "have",
|
||||
"succubus": "succubi",
|
||||
"child": "children"}.get(bits[-1], bits[-1] + "s")
|
||||
return " ".join(bits)
|
||||
|
||||
def singular(plural):
|
||||
# converse of plural above (kinda)
|
||||
# this is used to map plural team names back to singular,
|
||||
# so we don't need to worry about stuff like possessives
|
||||
# Note that this is currently only ever called on team names,
|
||||
# and will require adjustment if one wishes to use it on roles.
|
||||
conv = {"wolves": "wolf",
|
||||
"succubi": "succubus"}
|
||||
if plural in conv:
|
||||
return conv[plural]
|
||||
# otherwise we just added an s on the end
|
||||
return plural[:-1]
|
||||
|
||||
def list_players(roles = None):
|
||||
if roles is None:
|
||||
roles = var.ROLES.keys()
|
||||
pl = set()
|
||||
for x in roles:
|
||||
if x in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
continue
|
||||
for p in var.ROLES.get(x, ()):
|
||||
pl.add(p)
|
||||
return [p for p in var.ALL_PLAYERS if p in pl]
|
||||
|
||||
def list_players_and_roles():
|
||||
plr = {}
|
||||
for x in var.ROLES.keys():
|
||||
if x in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
continue # only get actual roles
|
||||
for p in var.ROLES[x]:
|
||||
plr[p] = x
|
||||
return plr
|
||||
|
||||
def get_role(p):
|
||||
for role, pl in var.ROLES.items():
|
||||
if role in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
continue # only get actual roles
|
||||
if p in pl:
|
||||
return role
|
||||
|
||||
def get_reveal_role(nick):
|
||||
if var.HIDDEN_TRAITOR and get_role(nick) == "traitor":
|
||||
role = var.DEFAULT_ROLE
|
||||
elif var.HIDDEN_AMNESIAC and nick in var.ORIGINAL_ROLES["amnesiac"]:
|
||||
role = "amnesiac"
|
||||
elif var.HIDDEN_CLONE and nick in var.ORIGINAL_ROLES["clone"]:
|
||||
role = "clone"
|
||||
elif nick in var.WILD_CHILDREN:
|
||||
role = "wild child"
|
||||
else:
|
||||
role = get_role(nick)
|
||||
|
||||
if var.ROLE_REVEAL != "team":
|
||||
return role
|
||||
|
||||
if role in var.WOLFTEAM_ROLES:
|
||||
return "wolf"
|
||||
elif role in var.TRUE_NEUTRAL_ROLES:
|
||||
return "neutral player"
|
||||
else:
|
||||
return "villager"
|
||||
|
||||
def get_templates(nick):
|
||||
tpl = []
|
||||
for x in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
try:
|
||||
if nick in var.ROLES[x]:
|
||||
tpl.append(x)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return tpl
|
||||
|
||||
role_order = lambda: var.ROLE_GUIDE
|
||||
|
||||
def break_long_message(phrases, joinstr = " "):
|
||||
message = []
|
||||
count = 0
|
||||
for phrase in phrases:
|
||||
# IRC max is 512, but freenode splits around 380ish, make 300 to have plenty of wiggle room
|
||||
if count + len(joinstr) + len(phrase) > 300:
|
||||
message.append("\n" + phrase)
|
||||
count = len(phrase)
|
||||
else:
|
||||
if not message:
|
||||
count = len(phrase)
|
||||
else:
|
||||
count += len(joinstr) + len(phrase)
|
||||
message.append(phrase)
|
||||
return joinstr.join(message)
|
||||
|
||||
class InvalidModeException(Exception): pass
|
||||
# vim: set sw=4 expandtab:
|
||||
|
690
src/wolfgame.py
690
src/wolfgame.py
File diff suppressed because it is too large
Load Diff
@ -52,7 +52,7 @@ import src
|
||||
from src import handler
|
||||
|
||||
def main():
|
||||
src.logger(None)("Connecting to {0}:{1}{2}".format(botconfig.HOST, "+" if botconfig.USE_SSL else "", botconfig.PORT))
|
||||
src.plog("Connecting to {0}:{1}{2}".format(botconfig.HOST, "+" if botconfig.USE_SSL else "", botconfig.PORT))
|
||||
cli = IRCClient(
|
||||
{"privmsg": handler.on_privmsg,
|
||||
"notice": lambda a, b, c, d: handler.on_privmsg(a, b, c, d, True),
|
||||
@ -77,4 +77,4 @@ if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception:
|
||||
src.logger("errors.log")(traceback.format_exc())
|
||||
src.errlog(traceback.format_exc())
|
||||
|
Loading…
Reference in New Issue
Block a user