This commit is contained in:
nyuszika7h 2014-07-19 19:05:49 +02:00
parent 6f16848302
commit 4189d5c874
11 changed files with 1644 additions and 1117 deletions

View File

@ -1,4 +1,5 @@
# The bot commands implemented in here are present no matter which module is loaded # The bot commands implemented in here are present no matter which module
# is loaded
import botconfig import botconfig
from tools import decorators from tools import decorators
@ -9,14 +10,16 @@ from settings import common as var
from base64 import b64encode from base64 import b64encode
import imp import imp
def on_privmsg(cli, rawnick, chan, msg, notice=False): def on_privmsg(cli, rawnick, chan, msg, notice=False):
currmod = ld.MODULES[ld.CURRENT_MODULE] currmod = ld.MODULES[ld.CURRENT_MODULE]
if botconfig.IGNORE_HIDDEN_COMMANDS and (chan.startswith("@#") or chan.startswith("+#")): if botconfig.IGNORE_HIDDEN_COMMANDS and (
chan.startswith("@#") or chan.startswith("+#")):
return return
if (notice and ((chan != botconfig.NICK and not botconfig.ALLOW_NOTICE_COMMANDS) or if (notice and ((chan != botconfig.NICK and not botconfig.ALLOW_NOTICE_COMMANDS) or (
(chan == botconfig.NICK and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))): chan == botconfig.NICK and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))):
return # not allowed in settings return # not allowed in settings
if chan != botconfig.NICK: # not a PM if chan != botconfig.NICK: # not a PM
@ -29,7 +32,9 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
raise e raise e
else: else:
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())
cli.msg(chan, "An error has occurred and has been logged.") cli.msg(
chan,
"An error has occurred and has been logged.")
# Now that is always called first. # Now that is always called first.
for x in set(list(COMMANDS.keys()) + (list(currmod.COMMANDS.keys()) if currmod else list())): for x in set(list(COMMANDS.keys()) + (list(currmod.COMMANDS.keys()) if currmod else list())):
if x and msg.lower().startswith(botconfig.CMD_CHAR + x): if x and msg.lower().startswith(botconfig.CMD_CHAR + x):
@ -43,7 +48,9 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
raise e raise e
else: else:
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())
cli.msg(chan, "An error has occurred and has been logged.") cli.msg(
chan,
"An error has occurred and has been logged.")
else: else:
for x in set(list(PM_COMMANDS.keys()) + (list(currmod.PM_COMMANDS.keys()) if currmod else list())): for x in set(list(PM_COMMANDS.keys()) + (list(currmod.PM_COMMANDS.keys()) if currmod else list())):
@ -62,15 +69,21 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
raise e raise e
else: else:
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())
cli.msg(chan, "An error has occurred and has been logged.") cli.msg(
chan,
"An error has occurred and has been logged.")
def __unhandled__(cli, prefix, cmd, *args): def __unhandled__(cli, prefix, cmd, *args):
currmod = ld.MODULES[ld.CURRENT_MODULE] currmod = ld.MODULES[ld.CURRENT_MODULE]
if cmd in set(list(HOOKS.keys())+(list(currmod.HOOKS.keys()) if currmod else list())): if cmd in set(
list(HOOKS.keys()) +
(list(currmod.HOOKS.keys()) if currmod else list())):
largs = list(args) largs = list(args)
for i, arg in enumerate(largs): for i, arg in enumerate(largs):
if isinstance(arg, bytes): largs[i] = arg.decode('ascii') if isinstance(arg, bytes):
largs[i] = arg.decode('ascii')
for fn in HOOKS.get(cmd, []) + (currmod.HOOKS.get(cmd, []) if currmod else []): for fn in HOOKS.get(cmd, []) + (currmod.HOOKS.get(cmd, []) if currmod else []):
try: try:
fn(cli, prefix, *largs) fn(cli, prefix, *largs)
@ -79,11 +92,15 @@ def __unhandled__(cli, prefix, cmd, *args):
raise e raise e
else: else:
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())
cli.msg(botconfig.CHANNEL, "An error has occurred and has been logged.") cli.msg(
botconfig.CHANNEL,
"An error has occurred and has been logged.")
else: else:
logging.debug('Unhandled command {0}({1})'.format(cmd, [arg.decode('utf_8') logging.debug(
for arg in args 'Unhandled command {0}({1})'.format(
if isinstance(arg, bytes)])) cmd, [
arg.decode('utf_8') for arg in args if isinstance(
arg, bytes)]))
COMMANDS = {} COMMANDS = {}
@ -94,6 +111,7 @@ cmd = decorators.generate(COMMANDS)
pmcmd = decorators.generate(PM_COMMANDS) pmcmd = decorators.generate(PM_COMMANDS)
hook = decorators.generate(HOOKS, raw_nick=True, permissions=False) hook = decorators.generate(HOOKS, raw_nick=True, permissions=False)
def connect_callback(cli): def connect_callback(cli):
def prepare_stuff(*args): def prepare_stuff(*args):
@ -134,10 +152,14 @@ def connect_callback(cli):
@hook("authenticate") @hook("authenticate")
def auth_plus(cli, something, plus): def auth_plus(cli, something, plus):
if plus == "+": if plus == "+":
nick_b = bytes(botconfig.USERNAME if botconfig.USERNAME else botconfig.NICK, "utf-8") nick_b = bytes(
botconfig.USERNAME if botconfig.USERNAME else botconfig.NICK,
"utf-8")
pass_b = bytes(botconfig.PASS, "utf-8") pass_b = bytes(botconfig.PASS, "utf-8")
secrt_msg = b'\0'.join((nick_b, nick_b, pass_b)) secrt_msg = b'\0'.join((nick_b, nick_b, pass_b))
cli.send("AUTHENTICATE " + b64encode(secrt_msg).decode("utf-8")) cli.send(
"AUTHENTICATE " +
b64encode(secrt_msg).decode("utf-8"))
@hook("cap") @hook("cap")
def on_cap(cli, svr, mynick, ack, cap): def on_cap(cli, svr, mynick, ack, cap):
@ -158,13 +180,11 @@ def connect_callback(cli):
"in botconfig.USERNAME if it's different from the bot nick?") "in botconfig.USERNAME if it's different from the bot nick?")
@hook("ping") @hook("ping")
def on_ping(cli, prefix, server): def on_ping(cli, prefix, server):
cli.send('PONG', server) cli.send('PONG', server)
if botconfig.DEBUG_MODE: if botconfig.DEBUG_MODE:
@cmd("module", admin_only=True) @cmd("module", admin_only=True)
def ch_module(cli, nick, chan, rest): def ch_module(cli, nick, chan, rest):

File diff suppressed because it is too large Load Diff

View File

@ -24,13 +24,18 @@ import ssl
from oyoyo.parse import parse_raw_irc_command from oyoyo.parse import parse_raw_irc_command
# Adapted from http://code.activestate.com/recipes/511490-implementation-of-the-token-bucket-algorithm/ # Adapted from
# http://code.activestate.com/recipes/511490-implementation-of-the-token-bucket-algorithm/
class TokenBucket(object): class TokenBucket(object):
"""An implementation of the token bucket algorithm. """An implementation of the token bucket algorithm.
>>> bucket = TokenBucket(80, 0.5) >>> bucket = TokenBucket(80, 0.5)
>>> bucket.consume(1) >>> bucket.consume(1)
""" """
def __init__(self, tokens, fill_rate): def __init__(self, tokens, fill_rate):
"""tokens is the total tokens in the bucket. fill_rate is the """tokens is the total tokens in the bucket. fill_rate is the
rate in tokens/second that the bucket will be refilled.""" rate in tokens/second that the bucket will be refilled."""
@ -57,7 +62,6 @@ class TokenBucket(object):
return self._tokens return self._tokens
def add_commands(d): def add_commands(d):
def dec(cls): def dec(cls):
for c in d: for c in d:
@ -68,12 +72,15 @@ def add_commands(d):
setattr(cls, c, func(c)) setattr(cls, c, func(c))
return cls return cls
return dec return dec
@add_commands(("join", @add_commands(("join",
"mode", "mode",
"nick", "nick",
"who", "who",
"cap")) "cap"))
class IRCClient(object): class IRCClient(object):
""" IRC Client class. This handles one connection to a server. """ IRC Client class. This handles one connection to a server.
This can be used either with or without IRCApp ( see connect() docs ) This can be used either with or without IRCApp ( see connect() docs )
""" """
@ -148,9 +155,14 @@ class IRCClient(object):
elif arg is None: elif arg is None:
continue continue
else: else:
raise Exception(('Refusing to send arg at index {1} of the args from '+ raise Exception(
'provided: {0}').format(repr([(type(arg), arg) ('Refusing to send arg at index {1} of the args from ' +
for arg in args]), i)) 'provided: {0}').format(
repr(
[
(type(arg),
arg) for arg in args]),
i))
msg = bytes(" ", "utf_8").join(bargs) msg = bytes(" ", "utf_8").join(bargs)
logging.info('---> send {0}'.format(str(msg)[1:])) logging.info('---> send {0}'.format(str(msg)[1:]))
@ -185,7 +197,9 @@ class IRCClient(object):
self.socket.setblocking(0) self.socket.setblocking(0)
if not self.sasl_auth: if not self.sasl_auth:
self.send("PASS {0}:{1}".format(self.authname if self.authname else self.nickname, self.send(
"PASS {0}:{1}".format(
self.authname if self.authname else self.nickname,
self.password if self.password else "NOPASS")) self.password if self.password else "NOPASS"))
else: else:
self.cap("LS") self.cap("LS")
@ -222,13 +236,22 @@ class IRCClient(object):
try: try:
enc = "utf8" enc = "utf8"
fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)] fargs = [
arg.decode(enc) for arg in args if isinstance(
arg,
bytes)]
except UnicodeDecodeError: except UnicodeDecodeError:
enc = "latin1" enc = "latin1"
fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)] fargs = [
arg.decode(enc) for arg in args if isinstance(
arg,
bytes)]
logging.debug("processCommand ({2}){0}({1})".format(command, logging.debug(
fargs, prefix)) "processCommand ({2}){0}({1})".format(
command,
fargs,
prefix))
try: try:
largs = list(args) largs = list(args)
if prefix is not None: if prefix is not None:
@ -236,9 +259,18 @@ class IRCClient(object):
# for i,arg in enumerate(largs): # for i,arg in enumerate(largs):
# if arg is not None: largs[i] = arg.decode(enc) # if arg is not None: largs[i] = arg.decode(enc)
if command in self.command_handler: if command in self.command_handler:
self.command_handler[command](self, prefix,*fargs) self.command_handler[command](
self,
prefix,
*
fargs)
elif "" in self.command_handler: elif "" in self.command_handler:
self.command_handler[""](self, prefix, command, *fargs) self.command_handler[""](
self,
prefix,
command,
*
fargs)
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
raise e # ? raise e # ?
@ -248,9 +280,11 @@ class IRCClient(object):
logging.info('closing socket') logging.info('closing socket')
self.socket.close() self.socket.close()
yield False yield False
def msg(self, user, msg): def msg(self, user, msg):
for line in msg.split('\n'): for line in msg.split('\n'):
maxchars = 494 - len(self.nickname+self.ident+self.hostmask+user) maxchars = 494 - len(
self.nickname + self.ident + self.hostmask + user)
while line: while line:
extra = "" extra = ""
if len(line) > maxchars: if len(line) > maxchars:
@ -259,9 +293,11 @@ class IRCClient(object):
self.send("PRIVMSG", user, ":{0}".format(line)) self.send("PRIVMSG", user, ":{0}".format(line))
line = extra line = extra
privmsg = msg # Same thing privmsg = msg # Same thing
def notice(self, user, msg): def notice(self, user, msg):
for line in msg.split('\n'): for line in msg.split('\n'):
maxchars = 495 - len(self.nickname+self.ident+self.hostmask+user) maxchars = 495 - len(
self.nickname + self.ident + self.hostmask + user)
while line: while line:
extra = "" extra = ""
if len(line) > maxchars: if len(line) > maxchars:
@ -269,27 +305,35 @@ class IRCClient(object):
line = line[:maxchars] line = line[:maxchars]
self.send("NOTICE", user, ":{0}".format(line)) self.send("NOTICE", user, ":{0}".format(line))
line = extra line = extra
def quit(self, msg=""): def quit(self, msg=""):
self.send("QUIT :{0}".format(msg)) self.send("QUIT :{0}".format(msg))
def part(self, chan, msg=""): def part(self, chan, msg=""):
self.send("PART {0} :{1}".format(chan, msg)) self.send("PART {0} :{1}".format(chan, msg))
def kick(self, chan, nick, msg=""): def kick(self, chan, nick, msg=""):
self.send("KICK", chan, nick, ":" + msg) self.send("KICK", chan, nick, ":" + msg)
def ns_identify(self, passwd): def ns_identify(self, passwd):
self.msg("NickServ", "IDENTIFY {0} {1}".format(self.nickname, passwd)) self.msg("NickServ", "IDENTIFY {0} {1}".format(self.nickname, passwd))
def ns_ghost(self): def ns_ghost(self):
self.msg("NickServ", "GHOST " + self.nickname) self.msg("NickServ", "GHOST " + self.nickname)
def ns_release(self): def ns_release(self):
self.msg("NickServ", "RELEASE " + self.nickname) self.msg("NickServ", "RELEASE " + self.nickname)
def ns_regain(self): def ns_regain(self):
self.msg("NickServ", "REGAIN " + self.nickname) self.msg("NickServ", "REGAIN " + self.nickname)
def user(self, uname, rname): def user(self, uname, rname):
self.send("USER", uname, self.host, self.host, self.send("USER", uname, self.host, self.host,
rname or uname) rname or uname)
def mainLoop(self): def mainLoop(self):
conn = self.connect() conn = self.connect()
while True: while True:
if not next(conn): if not next(conn):
print("Calling sys.exit()...") print("Calling sys.exit()...")
sys.exit() sys.exit()

View File

@ -208,4 +208,3 @@ protocol_events = [
] ]
all_events = generated_events + protocol_events + list(numeric_events.values()) all_events = generated_events + protocol_events + list(numeric_events.values())

View File

@ -21,6 +21,8 @@ from oyoyo.ircevents import generated_events, protocol_events,\
all_events, numeric_events all_events, numeric_events
# avoiding regex # avoiding regex
def parse_raw_irc_command(element): def parse_raw_irc_command(element):
""" """
This function parses a raw irc command and returns a tuple This function parses a raw irc command and returns a tuple
@ -56,7 +58,8 @@ def parse_raw_irc_command(element):
except KeyError: except KeyError:
logging.debug('unknown numeric event {0}'.format(command)) logging.debug('unknown numeric event {0}'.format(command))
command = command.lower() command = command.lower()
if isinstance(command, bytes): command = command.decode("utf_8") if isinstance(command, bytes):
command = command.decode("utf_8")
if args[0].startswith(bytes(':', 'utf_8')): if args[0].startswith(bytes(':', 'utf_8')):
args = [bytes(" ", "utf_8").join(args)[1:]] args = [bytes(" ", "utf_8").join(args)[1:]]
@ -89,4 +92,3 @@ def parse_nick(name):
return (nick, mode, rest, None) return (nick, mode, rest, None)
return (nick, mode, user, host) return (nick, mode, user, host)

View File

@ -31,25 +31,26 @@ GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 0
GUARDIAN_ANGEL_DIES_CHANCE = 1 / 2 GUARDIAN_ANGEL_DIES_CHANCE = 1 / 2
DETECTIVE_REVEALED_CHANCE = 2 / 5 DETECTIVE_REVEALED_CHANCE = 2 / 5
################################################################################################################# ##########################################################################
# ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ## # ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ##
################################################################################################################# ##########################################################################
ROLES_GUIDE = { 4 : ( 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ## ROLES_GUIDE = {4: (1, 1, 0, 0, 0, 0, 0, 0, 0, 0),
6 : ( 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 ), ## 6: (1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
8 : ( 1 , 2 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 ), ## 8: (1, 2, 1, 1, 1, 0, 0, 0, 0, 0),
10 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 ), ## 10: (1, 2, 1, 1, 1, 1, 1, 0, 0, 0),
11 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 ), ## 11: (1, 2, 1, 1, 1, 1, 1, 0, 1, 0),
15 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ## 15: (1, 3, 1, 1, 1, 1, 1, 0, 1, 1),
22 : ( 1 , 4 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ## 22: (1, 4, 1, 1, 1, 1, 1, 0, 1, 1),
29 : ( 1 , 5 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ## 29: (1, 5, 1, 1, 1, 1, 1, 0, 1, 1),
None : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )} ## None: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0)}
################################################################################################################# ##########################################################################
# Notes: ## # Notes: ##
################################################################################################################# ##########################################################################
GAME_MODES = {} GAME_MODES = {}
AWAY = [] # cloaks of people who are away. AWAY = [] # cloaks of people who are away.
SIMPLE_NOTIFY = [] # cloaks of people who !simple, who want everything /notice'd # cloaks of people who !simple, who want everything /notice'd
SIMPLE_NOTIFY = []
ROLE_INDICES = {0: "seer", ROLE_INDICES = {0: "seer",
1: "wolf", 1: "wolf",
@ -68,14 +69,17 @@ INDEX_OF_ROLE = dict((v,k) for k,v in ROLE_INDICES.items())
NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.", NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.",
"A pool of blood and wolf paw prints are found.", "A pool of blood and wolf paw prints are found.",
"Traces of wolf fur are found.") "Traces of wolf fur are found.")
LYNCH_MESSAGES = ("The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", LYNCH_MESSAGES = (
"The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
"Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", "Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
"The mob drags a protesting \u0002{0}\u0002 to the hanging tree. S/He succumbs to the will of the horde, and is hanged. It is discovered (s)he was a \u0002{1}\u0002.", "The mob drags a protesting \u0002{0}\u0002 to the hanging tree. S/He succumbs to the will of the horde, and is hanged. It is discovered (s)he was a \u0002{1}\u0002.",
"Resigned to his/her fate, \u0002{0}\u0002 is led to the gallows. After death, it is discovered (s)he was a \u0002{1}\u0002.") "Resigned to his/her fate, \u0002{0}\u0002 is led to the gallows. After death, it is discovered (s)he was a \u0002{1}\u0002.")
import botconfig import botconfig
RULES = (botconfig.CHANNEL + " channel rules: 1) Be nice to others. 2) Do not share information "+ RULES = (
botconfig.CHANNEL +
" channel rules: 1) Be nice to others. 2) Do not share information " +
"after death. 3) No bots allowed. 4) Do not play with clones.\n" + "after death. 3) No bots allowed. 4) Do not play with clones.\n" +
"5) Do not quit unless you need to leave. 6) No swearing and keep it " + "5) Do not quit unless you need to leave. 6) No swearing and keep it " +
"family-friendly. 7) Do not paste PM's from the bot during the game. " + "family-friendly. 7) Do not paste PM's from the bot during the game. " +
@ -83,17 +87,23 @@ RULES = (botconfig.CHANNEL + " channel rules: 1) Be nice to others. 2) Do not sh
# Other settings: # Other settings:
START_WITH_DAY = False START_WITH_DAY = False
WOLF_STEALS_GUN = False # at night, the wolf can steal steal the victim's bullets # at night, the wolf can steal steal the victim's bullets
WOLF_STEALS_GUN = False
OPT_IN_PING = False # instead of !away/!back, users can opt-in to be pinged OPT_IN_PING = False # instead of !away/!back, users can opt-in to be pinged
PING_IN = [] # cloaks of users who have opted in for ping PING_IN = [] # cloaks of users who have opted in for ping
is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol] is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol]
def plural(role): def plural(role):
if role == "wolf": return "wolves" if role == "wolf":
elif role == "person": return "people" return "wolves"
else: return role + "s" elif role == "person":
return "people"
else:
return role + "s"
def list_players(): def list_players():
pl = [] pl = []
@ -101,6 +111,7 @@ def list_players():
pl.extend(x) pl.extend(x)
return pl return pl
def list_players_and_roles(): def list_players_and_roles():
plr = {} plr = {}
for x in ROLES.keys(): for x in ROLES.keys():
@ -116,8 +127,10 @@ def del_player(pname):
ROLES[prole].remove(pname) ROLES[prole].remove(pname)
class InvalidModeException(Exception):
pass
class InvalidModeException(Exception): pass
def game_mode(name): def game_mode(name):
def decor(c): def decor(c):
GAME_MODES[name] = c GAME_MODES[name] = c
@ -137,11 +150,10 @@ CHANGEABLE_ROLES = { "seers" : INDEX_OF_ROLE["seer"],
"detectives": INDEX_OF_ROLE["detective"]} "detectives": INDEX_OF_ROLE["detective"]}
# TODO: implement game modes # TODO: implement game modes
@game_mode("roles") @game_mode("roles")
class ChangedRolesMode(object): class ChangedRolesMode(object):
"""Example: !fgame roles=wolves:1,seers:0,angels:1""" """Example: !fgame roles=wolves:1,seers:0,angels:1"""
def __init__(self, arg): def __init__(self, arg):
@ -164,7 +176,8 @@ class ChangedRolesMode(object):
raise InvalidModeException(("The role \u0002{0}\u0002 " + raise InvalidModeException(("The role \u0002{0}\u0002 " +
"is not valid.").format(role)) "is not valid.").format(role))
except ValueError: except ValueError:
raise InvalidModeException("A bad value was used in mode roles.") raise InvalidModeException(
"A bad value was used in mode roles.")
for k in ROLES_GUIDE.keys(): for k in ROLES_GUIDE.keys():
self.ROLES_GUIDE[k] = tuple(lx) self.ROLES_GUIDE[k] = tuple(lx)
@ -180,9 +193,11 @@ conn = sqlite3.connect("data.sqlite3", check_same_thread = False)
with conn: with conn:
c = conn.cursor() c = conn.cursor()
c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)') # whoops, i mean cloak, not nick # whoops, i mean cloak, not nick
c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)')
c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)') # people who understand each role # people who understand each role
c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)')
c.execute('SELECT * FROM away') c.execute('SELECT * FROM away')
for row in c: for row in c:
@ -194,13 +209,14 @@ with conn:
# populate the roles table # populate the roles table
c.execute('DROP TABLE IF EXISTS roles') c.execute('DROP TABLE IF EXISTS roles')
c.execute('CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)') c.execute(
'CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)')
for x in ["villager"] + list(ROLE_INDICES.values()): for x in ["villager"] + list(ROLE_INDICES.values()):
c.execute("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,)) c.execute("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,))
c.execute(
c.execute(('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, '+ ('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, ' +
'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, ' + 'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, ' +
'UNIQUE(player, role))')) 'UNIQUE(player, role))'))
@ -216,21 +232,27 @@ def remove_away(clk):
with conn: with conn:
c.execute('DELETE from away where nick=?', (clk,)) c.execute('DELETE from away where nick=?', (clk,))
def add_away(clk): def add_away(clk):
with conn: with conn:
c.execute('INSERT into away VALUES (?)', (clk,)) c.execute('INSERT into away VALUES (?)', (clk,))
def remove_simple_rolemsg(clk): def remove_simple_rolemsg(clk):
with conn: with conn:
c.execute('DELETE from simple_role_notify where cloak=?', (clk,)) c.execute('DELETE from simple_role_notify where cloak=?', (clk,))
def add_simple_rolemsg(clk): def add_simple_rolemsg(clk):
with conn: with conn:
c.execute('INSERT into simple_role_notify VALUES (?)', (clk,)) c.execute('INSERT into simple_role_notify VALUES (?)', (clk,))
def remove_ping(clk): def remove_ping(clk):
with conn: with conn:
c.execute('DELETE from ping where cloak=?', (clk,)) c.execute('DELETE from ping where cloak=?', (clk,))
def add_ping(clk): def add_ping(clk):
with conn: with conn:
c.execute('INSERT into ping VALUES (?)', (clk,)) c.execute('INSERT into ping VALUES (?)', (clk,))
@ -241,8 +263,10 @@ def update_role_stats(acc, role, won, iwon):
with conn: with conn:
wins, iwins, totalgames = 0, 0, 0 wins, iwins, totalgames = 0, 0, 0
c.execute(("SELECT teamwins, individualwins, totalgames FROM rolestats "+ c.execute(
"WHERE player=? AND role=?"), (acc, role)) ("SELECT teamwins, individualwins, totalgames FROM rolestats " +
"WHERE player=? AND role=?"),
(acc, role))
row = c.fetchone() row = c.fetchone()
if row: if row:
wins, iwins, total = row wins, iwins, total = row
@ -257,6 +281,3 @@ def update_role_stats(acc, role, won, iwon):
c.execute("INSERT OR REPLACE INTO rolestats VALUES (?,?,?,?,?)", c.execute("INSERT OR REPLACE INTO rolestats VALUES (?,?,?,?,?)",
(acc, role, wins, iwins, total)) (acc, role, wins, iwins, total))

View File

@ -28,7 +28,8 @@ KILL_IDLE_TIME = 300
WARN_IDLE_TIME = 180 WARN_IDLE_TIME = 180
PART_GRACE_TIME = 30 PART_GRACE_TIME = 30
QUIT_GRACE_TIME = 30 QUIT_GRACE_TIME = 30
# controls how many people it does in one /msg; only works for messages that are the same # controls how many people it does in one /msg; only works for messages
# that are the same
MAX_PRIVMSG_TARGETS = 4 MAX_PRIVMSG_TARGETS = 4
LEAVE_STASIS_PENALTY = 1 LEAVE_STASIS_PENALTY = 1
IDLE_STASIS_PENALTY = 1 IDLE_STASIS_PENALTY = 1
@ -56,26 +57,27 @@ GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 1/4
GUARDIAN_ANGEL_DIES_CHANCE = 1 / 2 GUARDIAN_ANGEL_DIES_CHANCE = 1 / 2
DETECTIVE_REVEALED_CHANCE = 2 / 5 DETECTIVE_REVEALED_CHANCE = 2 / 5
################################################################################################################# ##########################################################################
# ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ## # ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ##
################################################################################################################# ##########################################################################
ROLES_GUIDE = { 4 : ( 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ## ROLES_GUIDE = {4: (1, 1, 0, 0, 0, 0, 0, 0, 0, 0),
6 : ( 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ## 6: (1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
8 : ( 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 ), ## 8: (1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
10 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 ), ## 10: (1, 2, 1, 1, 1, 1, 1, 0, 0, 0),
12 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ), ## 12: (1, 2, 1, 1, 1, 1, 1, 1, 0, 1),
15 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ), ## 15: (1, 3, 1, 1, 1, 1, 1, 1, 0, 1),
17 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ## 17: (1, 3, 1, 1, 1, 1, 1, 1, 1, 1),
18 : ( 1 , 3 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ## 18: (1, 3, 2, 1, 1, 1, 1, 1, 1, 1),
20 : ( 1 , 4 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ## 20: (1, 4, 2, 1, 1, 1, 1, 1, 1, 1),
None : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )} ## None: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0)}
################################################################################################################# ##########################################################################
# Notes: ## # Notes: ##
################################################################################################################# ##########################################################################
GAME_MODES = {} GAME_MODES = {}
AWAY = ['services.', 'services.int'] # cloaks of people who are away. AWAY = ['services.', 'services.int'] # cloaks of people who are away.
SIMPLE_NOTIFY = [] # cloaks of people who !simple, who want everything /notice'd # cloaks of people who !simple, who want everything /notice'd
SIMPLE_NOTIFY = []
ROLE_INDICES = {0: "seer", ROLE_INDICES = {0: "seer",
1: "wolf", 1: "wolf",
@ -93,7 +95,8 @@ INDEX_OF_ROLE = dict((v,k) for k,v in ROLE_INDICES.items())
NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.", NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.",
"A pool of blood and wolf paw prints are found.", "A pool of blood and wolf paw prints are found.",
"Traces of wolf fur are found.") "Traces of wolf fur are found.")
LYNCH_MESSAGES = ("The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", LYNCH_MESSAGES = (
"The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
"Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", "Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
"Despite protests, the mob drags their victim to the hanging tree. \u0002{0}\u0002 succumbs to the will of the horde, and is hanged. The villagers have killed a \u0002{1}\u0002.", "Despite protests, the mob drags their victim to the hanging tree. \u0002{0}\u0002 succumbs to the will of the horde, and is hanged. The villagers have killed a \u0002{1}\u0002.",
"Resigned to the inevitable, \u0002{0}\u0002 is led to the gallows. Once the twitching stops, it is discovered that the village lynched a \u0002{1}\u0002.", "Resigned to the inevitable, \u0002{0}\u0002 is led to the gallows. Once the twitching stops, it is discovered that the village lynched a \u0002{1}\u0002.",
@ -105,17 +108,23 @@ RULES = (botconfig.CHANNEL + " channel rules: http://wolf.xnrand.com/rules")
# Other settings: # Other settings:
START_WITH_DAY = False START_WITH_DAY = False
WOLF_STEALS_GUN = True # at night, the wolf can steal steal the victim's bullets # at night, the wolf can steal steal the victim's bullets
WOLF_STEALS_GUN = True
OPT_IN_PING = False # instead of !away/!back, users can opt-in to be pinged OPT_IN_PING = False # instead of !away/!back, users can opt-in to be pinged
PING_IN = [] # cloaks of users who have opted in for ping PING_IN = [] # cloaks of users who have opted in for ping
is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol] is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol]
def plural(role): def plural(role):
if role == "wolf": return "wolves" if role == "wolf":
elif role == "person": return "people" return "wolves"
else: return role + "s" elif role == "person":
return "people"
else:
return role + "s"
def list_players(): def list_players():
pl = [] pl = []
@ -123,6 +132,7 @@ def list_players():
pl.extend(x) pl.extend(x)
return pl return pl
def list_players_and_roles(): def list_players_and_roles():
plr = {} plr = {}
for x in ROLES.keys(): for x in ROLES.keys():
@ -132,19 +142,23 @@ def list_players_and_roles():
get_role = lambda plyr: list_players_and_roles()[plyr] get_role = lambda plyr: list_players_and_roles()[plyr]
def get_reveal_role(nick): def get_reveal_role(nick):
if HIDDEN_TRAITOR and get_role(nick) == "traitor": if HIDDEN_TRAITOR and get_role(nick) == "traitor":
return "villager" return "villager"
else: else:
return get_role(nick) return get_role(nick)
def del_player(pname): def del_player(pname):
prole = get_role(pname) prole = get_role(pname)
ROLES[prole].remove(pname) ROLES[prole].remove(pname)
class InvalidModeException(Exception):
pass
class InvalidModeException(Exception): pass
def game_mode(name): def game_mode(name):
def decor(c): def decor(c):
GAME_MODES[name] = c GAME_MODES[name] = c
@ -164,11 +178,10 @@ CHANGEABLE_ROLES = { "seers" : INDEX_OF_ROLE["seer"],
"detectives": INDEX_OF_ROLE["detective"]} "detectives": INDEX_OF_ROLE["detective"]}
# TODO: implement game modes # TODO: implement game modes
@game_mode("roles") @game_mode("roles")
class ChangedRolesMode(object): class ChangedRolesMode(object):
"""Example: !fgame roles=wolves:1,seers:0,angels:1""" """Example: !fgame roles=wolves:1,seers:0,angels:1"""
def __init__(self, arg): def __init__(self, arg):
@ -190,7 +203,8 @@ class ChangedRolesMode(object):
raise InvalidModeException(("The role \u0002{0}\u0002 " + raise InvalidModeException(("The role \u0002{0}\u0002 " +
"is not valid.").format(role)) "is not valid.").format(role))
except ValueError: except ValueError:
raise InvalidModeException("A bad value was used in mode roles.") raise InvalidModeException(
"A bad value was used in mode roles.")
for k in ROLES_GUIDE.keys(): for k in ROLES_GUIDE.keys():
self.ROLES_GUIDE[k] = tuple(lx) self.ROLES_GUIDE[k] = tuple(lx)
@ -205,9 +219,11 @@ conn = sqlite3.connect("data.sqlite3", check_same_thread = False)
with conn: with conn:
c = conn.cursor() c = conn.cursor()
c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)') # whoops, i mean cloak, not nick # whoops, i mean cloak, not nick
c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)')
c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)') # people who understand each role # people who understand each role
c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)')
c.execute('SELECT * FROM away') c.execute('SELECT * FROM away')
for row in c: for row in c:
@ -219,21 +235,21 @@ with conn:
# populate the roles table # populate the roles table
c.execute('DROP TABLE IF EXISTS roles') c.execute('DROP TABLE IF EXISTS roles')
c.execute('CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)') c.execute(
'CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)')
for x in ["villager"] + list(ROLE_INDICES.values()): for x in ["villager"] + list(ROLE_INDICES.values()):
c.execute("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,)) c.execute("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,))
c.execute(
c.execute(('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, '+ ('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, ' +
'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, ' + 'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, ' +
'UNIQUE(player, role))')) 'UNIQUE(player, role))'))
c.execute(
c.execute(('CREATE TABLE IF NOT EXISTS gamestats (size SMALLINT, villagewins SMALLINT, ' + ('CREATE TABLE IF NOT EXISTS gamestats (size SMALLINT, villagewins SMALLINT, ' +
'wolfwins SMALLINT, totalgames SMALLINT, UNIQUE(size))')) 'wolfwins SMALLINT, totalgames SMALLINT, UNIQUE(size))'))
if OPT_IN_PING: if OPT_IN_PING:
c.execute('CREATE TABLE IF NOT EXISTS ping (cloak text)') c.execute('CREATE TABLE IF NOT EXISTS ping (cloak text)')
@ -246,22 +262,27 @@ def remove_away(clk):
with conn: with conn:
c.execute('DELETE from away where nick=?', (clk,)) c.execute('DELETE from away where nick=?', (clk,))
def add_away(clk): def add_away(clk):
with conn: with conn:
c.execute('INSERT into away VALUES (?)', (clk,)) c.execute('INSERT into away VALUES (?)', (clk,))
def remove_simple_rolemsg(clk): def remove_simple_rolemsg(clk):
with conn: with conn:
c.execute('DELETE from simple_role_notify where cloak=?', (clk,)) c.execute('DELETE from simple_role_notify where cloak=?', (clk,))
def add_simple_rolemsg(clk): def add_simple_rolemsg(clk):
with conn: with conn:
c.execute('INSERT into simple_role_notify VALUES (?)', (clk,)) c.execute('INSERT into simple_role_notify VALUES (?)', (clk,))
def remove_ping(clk): def remove_ping(clk):
with conn: with conn:
c.execute('DELETE from ping where cloak=?', (clk,)) c.execute('DELETE from ping where cloak=?', (clk,))
def add_ping(clk): def add_ping(clk):
with conn: with conn:
c.execute('INSERT into ping VALUES (?)', (clk,)) c.execute('INSERT into ping VALUES (?)', (clk,))
@ -271,8 +292,10 @@ def update_role_stats(acc, role, won, iwon):
with conn: with conn:
wins, iwins, total = 0, 0, 0 wins, iwins, total = 0, 0, 0
c.execute(("SELECT teamwins, individualwins, totalgames FROM rolestats "+ c.execute(
"WHERE player=? AND role=?"), (acc, role)) ("SELECT teamwins, individualwins, totalgames FROM rolestats " +
"WHERE player=? AND role=?"),
(acc, role))
row = c.fetchone() row = c.fetchone()
if row: if row:
wins, iwins, total = row wins, iwins, total = row
@ -286,6 +309,7 @@ def update_role_stats(acc, role, won, iwon):
c.execute("INSERT OR REPLACE INTO rolestats VALUES (?,?,?,?,?)", c.execute("INSERT OR REPLACE INTO rolestats VALUES (?,?,?,?,?)",
(acc, role, wins, iwins, total)) (acc, role, wins, iwins, total))
def update_game_stats(size, winner): def update_game_stats(size, winner):
with conn: with conn:
vwins, wwins, total = 0, 0, 0 vwins, wwins, total = 0, 0, 0
@ -305,51 +329,81 @@ def update_game_stats(size, winner):
c.execute("INSERT OR REPLACE INTO gamestats VALUES (?,?,?,?)", c.execute("INSERT OR REPLACE INTO gamestats VALUES (?,?,?,?)",
(size, vwins, wwins, total)) (size, vwins, wwins, total))
def get_player_stats(acc, role): def get_player_stats(acc, role):
if role.lower() not in ["villager"] + [v.lower() for k, v in ROLE_INDICES.items()]: if role.lower() not in ["villager"] + [v.lower()
for k, v in ROLE_INDICES.items()]:
return "No such role: {0}".format(role) return "No such role: {0}".format(role)
with conn: with conn:
c.execute("SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", (acc,)) c.execute(
"SELECT player FROM rolestats WHERE player=? COLLATE NOCASE",
(acc,))
player = c.fetchone() player = c.fetchone()
if player: if player:
for row in c.execute("SELECT * FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE", (acc, role)): for row in c.execute("SELECT * FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE", (acc, role)):
msg = "\u0002{0}\u0002 as \u0002{1}\u0002 | Team wins: {2} (%d%%), Individual wins: {3} (%d%%), Total games: {4}".format(*row) msg = "\u0002{0}\u0002 as \u0002{1}\u0002 | Team wins: {2} (%d%%), Individual wins: {3} (%d%%), Total games: {4}".format(
return msg % (round(row[2]/row[4] * 100), round(row[3]/row[4] * 100)) *
row)
return msg % (
round(row[2] / row[4] * 100),
round(row[3] / row[4] * 100))
else: else:
return "No stats for {0} as {1}.".format(player[0], role) return "No stats for {0} as {1}.".format(player[0], role)
return "{0} has not played any games.".format(acc) return "{0} has not played any games.".format(acc)
def get_player_totals(acc): def get_player_totals(acc):
role_totals = [] role_totals = []
with conn: with conn:
c.execute("SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", (acc,)) c.execute(
"SELECT player FROM rolestats WHERE player=? COLLATE NOCASE",
(acc,))
player = c.fetchone() player = c.fetchone()
if player: if player:
for role in ["villager"] + [v for k, v in ROLE_INDICES.items()]: for role in ["villager"] + [v for k, v in ROLE_INDICES.items()]:
c.execute("SELECT totalgames FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE", (acc, role)) c.execute(
"SELECT totalgames FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE",
(acc, role))
row = c.fetchone() row = c.fetchone()
if row: if row:
role_totals.append("\u0002{0}\u0002: {1}".format(role, *row)) role_totals.append(
c.execute("SELECT SUM(totalgames) from rolestats WHERE player=? COLLATE NOCASE AND role!='cursed villager' AND role!='gunner'", (acc,)) "\u0002{0}\u0002: {1}".format(
role,
*
row))
c.execute(
"SELECT SUM(totalgames) from rolestats WHERE player=? COLLATE NOCASE AND role!='cursed villager' AND role!='gunner'",
(acc,))
row = c.fetchone() row = c.fetchone()
return "\u0002{0}\u0002's totals | \u0002{1}\u0002 games | {2}".format(player[0], row[0], ", ".join(role_totals)) return "\u0002{0}\u0002's totals | \u0002{1}\u0002 games | {2}".format(
player[0],
row[0],
", ".join(role_totals))
else: else:
return "\u0002{0}\u0002 has not played any games.".format(acc) return "\u0002{0}\u0002 has not played any games.".format(acc)
def get_game_stats(size): def get_game_stats(size):
with conn: with conn:
for row in c.execute("SELECT * FROM gamestats WHERE size=?", (size,)): for row in c.execute("SELECT * FROM gamestats WHERE size=?", (size,)):
msg = "\u0002{0}\u0002 player games | Village wins: {1} (%d%%), Wolf wins: {2} (%d%%), Total games: {3}".format(*row) msg = "\u0002{0}\u0002 player games | Village wins: {1} (%d%%), Wolf wins: {2} (%d%%), Total games: {3}".format(
return msg % (round(row[1]/row[3] * 100), round(row[2]/row[3] * 100)) *
row)
return msg % (
round(row[1] / row[3] * 100),
round(row[2] / row[3] * 100))
else: else:
return "No stats for \u0002{0}\u0002 player games.".format(size) return "No stats for \u0002{0}\u0002 player games.".format(size)
def get_game_totals(): def get_game_totals():
size_totals = [] size_totals = []
total = 0 total = 0
with conn: with conn:
for size in range(MIN_PLAYERS, MAX_PLAYERS + 1): for size in range(MIN_PLAYERS, MAX_PLAYERS + 1):
c.execute("SELECT size, totalgames FROM gamestats WHERE size=?", (size,)) c.execute(
"SELECT size, totalgames FROM gamestats WHERE size=?",
(size,))
row = c.fetchone() row = c.fetchone()
if row: if row:
size_totals.append("\u0002{0}p\u0002: {1}".format(*row)) size_totals.append("\u0002{0}p\u0002: {1}".format(*row))

View File

@ -1,17 +1,30 @@
# Copyright (c) 2011, Jimmy Cao # Copyright (c) 2011, Jimmy Cao
# All rights reserved. # All rights reserved.
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. # Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. # Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from oyoyo.parse import parse_nick from oyoyo.parse import parse_nick
import fnmatch import fnmatch
import botconfig import botconfig
def generate(fdict, permissions=True, **kwargs): def generate(fdict, permissions=True, **kwargs):
"""Generates a decorator generator. Always use this""" """Generates a decorator generator. Always use this"""
def cmd(*s, raw_nick=False, admin_only=False, owner_only=False, hookid=-1): def cmd(*s, raw_nick=False, admin_only=False, owner_only=False, hookid=-1):
@ -37,7 +50,9 @@ def generate(fdict, permissions=True, **kwargs):
if fnmatch.fnmatch(cloak.lower(), pattern.lower()): if fnmatch.fnmatch(cloak.lower(), pattern.lower()):
for cmdname in s: for cmdname in s:
if cmdname in botconfig.DENY[pattern]: if cmdname in botconfig.DENY[pattern]:
largs[0].notice(nick, "You do not have permission to use that command.") largs[0].notice(
nick,
"You do not have permission to use that command.")
return return
for pattern in botconfig.ALLOW.keys(): for pattern in botconfig.ALLOW.keys():
if fnmatch.fnmatch(cloak.lower(), pattern.lower()): if fnmatch.fnmatch(cloak.lower(), pattern.lower()):
@ -45,15 +60,20 @@ def generate(fdict, permissions=True, **kwargs):
if cmdname in botconfig.ALLOW[pattern]: if cmdname in botconfig.ALLOW[pattern]:
return f(*largs) # no questions return f(*largs) # no questions
if owner_only: if owner_only:
if cloak and [ptn for ptn in botconfig.OWNERS if cloak and [
if fnmatch.fnmatch(cloak.lower(), ptn.lower())]: ptn for ptn in botconfig.OWNERS if fnmatch.fnmatch(
cloak.lower(),
ptn.lower())]:
return f(*largs) return f(*largs)
elif cloak: elif cloak:
largs[0].notice(nick, "You are not the owner.") largs[0].notice(nick, "You are not the owner.")
return return
if admin_only: if admin_only:
if cloak and [ptn for ptn in botconfig.ADMINS+botconfig.OWNERS if cloak and[ptn
if fnmatch.fnmatch(cloak.lower(), ptn.lower())]: for ptn in botconfig.ADMINS + botconfig.OWNERS
if fnmatch.fnmatch(
cloak.lower(),
ptn.lower())]:
return f(*largs) return f(*largs)
elif cloak: elif cloak:
largs[0].notice(nick, "You are not an admin.") largs[0].notice(nick, "You are not an admin.")
@ -68,7 +88,10 @@ def generate(fdict, permissions=True, **kwargs):
for fn in fdict[x]: for fn in fdict[x]:
if (fn.owner_only != owner_only or if (fn.owner_only != owner_only or
fn.admin_only != admin_only): fn.admin_only != admin_only):
raise Exception("Command: "+x+" has non-matching protection levels!") raise Exception(
"Command: " +
x +
" has non-matching protection levels!")
fdict[x].append(innerf) fdict[x].append(innerf)
if alias: if alias:
innerf.aliases.append(x) innerf.aliases.append(x)
@ -82,7 +105,8 @@ def generate(fdict, permissions=True, **kwargs):
return dec return dec
return lambda *args, **kwarargs: cmd(*args, **kwarargs) if kwarargs else cmd(*args, **kwargs) return lambda *args, **kwarargs: cmd(*args, **
kwarargs) if kwarargs else cmd(*args, **kwargs)
def unhook(hdict, hookid): def unhook(hdict, hookid):

View File

@ -1,6 +1,7 @@
import botconfig import botconfig
from datetime import datetime from datetime import datetime
class WolfgameLogger(object): class WolfgameLogger(object):
def __init__(self, outfile, boutfile): def __init__(self, outfile, boutfile):
@ -17,13 +18,16 @@ class WolfgameLogger(object):
self.barelogged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + " ".join(args) + "\n" self.barelogged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + " ".join(args) + "\n"
def logChannelMessage(self, who, message): def logChannelMessage(self, who, message):
self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + "<{0}> {1}\n".format(who, message) self.logged += datetime.utcnow().strftime(
"%Y-%m-%d %H:%M:%S ") + "<{0}> {1}\n".format(who, message)
def logCommand(self, who, cmd, rest): def logCommand(self, who, cmd, rest):
self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + "<{0}> {1}{2} {3}".format(who, botconfig.CMD_CHAR, cmd, rest) + "\n" self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + \
"<{0}> {1}{2} {3}".format(who, botconfig.CMD_CHAR, cmd, rest) + "\n"
def logMessage(self, message): def logMessage(self, message):
self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + "<{0}> ".format(botconfig.NICK)+message+"\n" self.logged += datetime.utcnow().strftime(
"%Y-%m-%d %H:%M:%S ") + "<{0}> ".format(botconfig.NICK) + message + "\n"
def saveToFile(self): def saveToFile(self):
if self.outfile: if self.outfile: